Compare commits

..

1 Commits

Author SHA1 Message Date
Kovid Goyal
4e6b26dd7e Finish up implementation of drawing curve with derivative
Fixes #8299
2025-05-10 08:37:17 +05:30
634 changed files with 30473 additions and 73970 deletions

5
.gitattributes vendored
View File

@@ -1,7 +1,3 @@
kitty/terminfo.h linguist-generated=true
terminfo/kitty.termcap linguist-generated=true
terminfo/kitty.terminfo linguist-generated=true
terminfo/x/xterm-kitty linguist-generated=true
kitty/char-props-data.h linguist-generated=true
kitty_tests/GraphemeBreakTest.json linguist-generated=true
kitty/charsets.c linguist-generated=true
@@ -11,7 +7,6 @@ kitty/rgb.py linguist-generated=true
kitty/srgb_gamma.* linguist-generated=true
kitty/gl-wrapper.* linguist-generated=true
kitty/glfw-wrapper.* linguist-generated=true
kitty/color-names.h linguist-generated=true
kitty/parse-graphics-command.h linguist-generated=true
kitty/parse-multicell-command.h linguist-generated=true
kitty/options/types.py linguist-generated=true

View File

@@ -1,35 +0,0 @@
Before implementing any code changes or responding to a request, run the
following command:
python3 .github/workflows/ci.py build
This will download needed dependencies, then create all generated files
and build the project, making it ready for inspection.
# Repository Build & Test Instructions
## Build Procedures
- **Build command:** Run `./dev.sh build` to build the project
- Run `gen/config.py` to update generated config parsing code for both kitty
and kitten. The `gen/go_code.py` generator is run automatically by the build
command to keep generated Go code files up to date.
- To build individual kittens use the build command above **do not** try to run go build
yourself.
Once a build is done, the kitty and kitten binaries will be in the `kitty/launcher` directory.
Note that the kitty binary can run python files using `kitty +launch file.py`.
When it does so the kitty fast_data_types module is available to the python
code.
## Test Procedures
- To run the complete test suite, run `./test.py`
- To run a specific test, run `./test.py test_name` where `test_name` is the name of the test without the
leading `test_` for Python tests and without the leading `Test` for Go tests.
- Do not use go test or ./setup.py test to run tests
## PR guidance
When creating a pull request, if the changes in the pull request are user
visible compared to the previous kitty release, add an entry in the file docs/changelog.rst
describing the change. If the changes include changes to Go code, run gofmt on
the changed Go files to ensure they are formatted correctly, before submitting
your changes.

View File

@@ -13,15 +13,3 @@ updates:
all-go-deps:
patterns:
- "*" # group all non-security update PRs
cooldown:
default-days: 7
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
groups:
actions:
patterns:
- "*"
cooldown:
default-days: 7

View File

@@ -4,33 +4,24 @@
import glob
import io
import json
import lzma
import os
import platform
import shlex
import shutil
import subprocess
import sys
import tarfile
import time
from urllib.request import Request, urlopen
from urllib.request import urlopen
BUNDLE_URL = 'https://download.calibre-ebook.com/ci/kitty/{}-64.tar.xz'
FONTS_URL = 'https://download.calibre-ebook.com/ci/fonts.tar.xz'
NERD_URL = 'https://github.com/ryanoasis/nerd-fonts/releases/latest/download/NerdFontsSymbolsOnly.tar.xz'
is_bundle = os.environ.get('KITTY_BUNDLE') == '1'
is_codeql = os.environ.get('KITTY_CODEQL') == '1'
is_macos = 'darwin' in sys.platform.lower()
running_under_sanitizer = os.environ.get('KITTY_SANITIZE') == '1'
SW = ''
SLANG_INSTALL_DIR = '/tmp/slang'
def do_print_crash_reports() -> None:
sys.stdout.flush()
sys.stderr.flush()
time.sleep(2)
print('Printing available crash reports...')
if is_macos:
end_time = time.monotonic() + 90
@@ -70,25 +61,9 @@ def run(*a: str, print_crash_reports: bool = False) -> None:
raise SystemExit(f'The following process failed with exit code: {ret}:\n{cmd}')
def download_with_retry(url: str | Request, count: int = 5) -> bytes:
for i in range(count):
try:
print('Downloading', getattr(url, 'full_url', url), flush=True)
with urlopen(url) as f:
ans: bytes = f.read()
return ans
except Exception as err:
if getattr(err, 'code', -1) == 403:
raise
if i >= count - 1:
raise
print(f'Download failed with error {err} retrying...', file=sys.stderr)
time.sleep(1)
return b''
def install_fonts() -> None:
data = download_with_retry(FONTS_URL)
with urlopen(FONTS_URL) as f:
data = f.read()
fonts_dir = os.path.expanduser('~/Library/Fonts' if is_macos else '~/.local/share/fonts')
os.makedirs(fonts_dir, exist_ok=True)
with tarfile.open(fileobj=io.BytesIO(data), mode='r:xz') as tf:
@@ -96,7 +71,8 @@ def install_fonts() -> None:
tf.extractall(fonts_dir, filter='fully_trusted')
except TypeError:
tf.extractall(fonts_dir)
data = download_with_retry(NERD_URL)
with urlopen(NERD_URL) as f:
data = f.read()
with tarfile.open(fileobj=io.BytesIO(data), mode='r:xz') as tf:
try:
tf.extractall(fonts_dir, filter='fully_trusted')
@@ -104,52 +80,23 @@ def install_fonts() -> None:
tf.extractall(fonts_dir)
def install_slang_compiler() -> None:
os_name = 'macos' if is_macos else 'linux'
machine = platform.machine().lower()
arch = 'aarch64' if machine in ('aarch64', 'arm64') else 'x86_64'
with open('bypy/sources.json') as f:
data = json.loads(f.read())
for dep in data:
if dep['name'].startswith('slang '):
version = dep['name'].split()[-1]
break
url = f'https://github.com/shader-slang/slang/releases/download/v{version}/slang-{version}-{os_name}-{arch}.tar.gz'
install_dir = SLANG_INSTALL_DIR
os.makedirs(install_dir, exist_ok=True)
data = download_with_retry(url)
with tarfile.open(fileobj=io.BytesIO(data), mode='r:gz') as tf:
try:
tf.extractall(install_dir, filter='fully_trusted')
except TypeError:
# filter parameter not supported on older Python versions
tf.extractall(install_dir)
pc_dir = os.path.join(install_dir, 'lib', 'pkgconfig')
pp = os.environ.get('PKG_CONFIG_PATH', '')
os.environ['PKG_CONFIG_PATH'] = f'{pc_dir}:{pp}' if pp else pc_dir
def install_deps() -> None:
print('Installing kitty dependencies...')
sys.stdout.flush()
if is_macos:
if not is_codeql: # for some reason brew fails on CodeQL we dont need it anyway
items = [x.split()[1].strip('"') for x in open('Brewfile').readlines() if x.strip().startswith('brew ')]
openssl = 'openssl'
items.remove('go') # already installed by ci.yml
import ssl
if ssl.OPENSSL_VERSION_INFO[0] == 1:
openssl += '@1.1'
run('brew', 'install', 'fish', openssl, *items)
items = [x.split()[1].strip('"') for x in open('Brewfile').readlines() if x.strip().startswith('brew ')]
openssl = 'openssl'
items.remove('go') # already installed by ci.yml
import ssl
if ssl.OPENSSL_VERSION_INFO[0] == 1:
openssl += '@1.1'
run('brew', 'install', 'fish', openssl, *items)
else:
run('sudo apt-get update')
run('sudo apt-get install -y --fix-missing libgl1-mesa-dev libxi-dev libxrandr-dev libxinerama-dev ca-certificates'
run('sudo 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'
' libwayland-dev wayland-protocols glslang-tools')
' libsimde-dev libsystemd-dev libcairo2-dev zsh bash dash systemd-coredump gdb')
# for some reason these directories are world writable which causes zsh
# compinit to break
run('sudo chmod -R og-w /usr/share/zsh')
@@ -160,21 +107,16 @@ def install_deps() -> None:
if sys.version_info[:2] < (3, 7):
cmd += ' importlib-resources dataclasses'
run(cmd)
install_slang_compiler()
install_fonts()
def set_slangc() -> None:
slangc = os.path.join(SW if is_bundle else SLANG_INSTALL_DIR, 'bin', 'slangc')
os.environ['SLANGC'] = slangc
def build_kitty() -> None:
python = shutil.which('python3') if is_bundle else sys.executable
cmd = f'{python} setup.py build --verbose'
if running_under_sanitizer:
if is_macos:
cmd += ' --debug' # for better crash report to debug SIGILL issue
if os.environ.get('KITTY_SANITIZE') == '1':
cmd += ' --debug --sanitize'
set_slangc()
run(cmd)
@@ -182,17 +124,12 @@ def test_kitty() -> None:
if is_macos:
run('ulimit -c unlimited')
run('sudo chmod -R 777 /cores')
if running_under_sanitizer:
os.environ['MallocNanoZone'] = '0'
set_slangc()
run('./test.py', print_crash_reports=True)
def package_kitty() -> None:
set_slangc()
python = 'python3' if is_macos else 'python'
run(f'{python} setup.py linux-package --update-check-interval=0 --verbose')
run('make FAIL_WARN=1 docs')
if is_macos:
run('python3 setup.py kitty.app --update-check-interval=0 --verbose')
run('kitty.app/Contents/MacOS/kitty +runpy "from kitty.constants import *; print(kitty_exe())"')
@@ -207,8 +144,7 @@ def replace_in_file(path: str, src: str, dest: str) -> None:
def setup_bundle_env() -> None:
global SW
os.environ['SW'] = SW = '/Users/Shared/kitty-build/sw/sw' if is_macos else os.path.join(
os.environ['GITHUB_WORKSPACE'], 'sw')
os.environ['SW'] = SW = '/Users/Shared/kitty-build/sw/sw' if is_macos else os.path.join(os.environ['GITHUB_WORKSPACE'], 'sw')
os.environ['PKG_CONFIG_PATH'] = os.path.join(SW, 'lib', 'pkgconfig')
if is_macos:
os.environ['PATH'] = '{}:{}'.format('/usr/local/opt/sphinx-doc/bin', os.environ['PATH'])
@@ -218,13 +154,12 @@ def setup_bundle_env() -> None:
os.environ['PATH'] = '{}:{}'.format(os.path.join(SW, 'bin'), os.environ['PATH'])
def install_bundle(dest: str = '', which: str = '') -> None:
dest = dest or SW
def install_bundle() -> None:
cwd = os.getcwd()
os.makedirs(dest, exist_ok=True)
os.chdir(dest)
which = which or ('macos' if is_macos else 'linux')
data = download_with_retry(BUNDLE_URL.format(which))
os.makedirs(SW)
os.chdir(SW)
with urlopen(BUNDLE_URL.format('macos' if is_macos else 'linux')) as f:
data = f.read()
with tarfile.open(fileobj=io.BytesIO(data), mode='r:xz') as tf:
try:
tf.extractall(filter='fully_trusted')
@@ -235,83 +170,13 @@ def install_bundle(dest: str = '', which: str = '') -> None:
for dirpath, dirnames, filenames in os.walk('.'):
for f in filenames:
if f.endswith('.pc') or (f.endswith('.py') and f.startswith('_sysconfig')):
replace_in_file(os.path.join(dirpath, f), '/sw/sw', dest)
replace_in_file(os.path.join(dirpath, f), '/sw/sw', SW)
replaced += 1
if replaced < 2:
raise SystemExit('Failed to replace path to SW in bundle')
os.chdir(cwd)
def install_grype(exe: str = '/tmp/grype') -> str:
raw = download_with_retry('https://download.calibre-ebook.com/ci/grype.xz')
raw = lzma.decompress(raw)
with open(exe, 'wb') as f:
f.write(raw)
os.fchmod(f.fileno(), 0o755)
subprocess.check_call([exe, 'db', 'update'])
return exe
IGNORED_DEPENDENCY_CVES = [
# Python stdlib
'CVE-2025-8194', # DoS in tarfile
'CVE-2025-6069', # DoS in HTMLParser
'CVE-2025-13836', # DoS in http client reading from malicious server
'CVE-2025-12084', # DoS in xml.dom.minidom unused in kitty
'CVE-2025-13837', # DoS in plistlib reading plist. We only use plistlib for writing
'CVE-2025-6075', # Quadratic complexity in os.path.expandvars()
# python stdlib all these are erroneously marked as fixed in python 3.15
# when it hasnt even been released. Sigh.
'CVE-2026-1299',
'CVE-2026-0865',
'CVE-2025-15282',
'CVE-2026-0672',
'CVE-2025-15366',
'CVE-2025-15367',
'CVE-2025-12781',
'CVE-2025-11468',
'CVE-2026-2297',
'CVE-2026-3644',
'CVE-2026-4224',
'CVE-2026-4519',
'CVE-2026-1502',
'CVE-2026-7210', # DoS in unused XML parser
'CVE-2026-3276', # DoS in unicodedata.normalize()
'CVE-2026-7774', # tarfile.data_filter path traversal bypass
# github.com/nwaples/rardecode/v2
'CVE-2025-11579', # rardecode is version 2.2.1, not vulnerable
'CVE-2026-2673', # openssl fix not released
]
def check_dependencies() -> None:
grype = install_grype()
with open((gc := os.path.expanduser('~/.grype.yml')), 'w') as f:
print('ignore:', file=f)
for x in IGNORED_DEPENDENCY_CVES:
print(' - vulnerability:', x, file=f)
dest = os.path.join(SW, 'linux')
os.makedirs(dest, exist_ok=True)
install_bundle(dest, os.path.basename(dest))
dest = os.path.join(SW, 'macos')
os.makedirs(dest, exist_ok=True)
install_bundle(dest, os.path.basename(dest))
cmdline = [grype, '--by-cve', '--config', gc, '--fail-on', 'medium', '--only-fixed', '--add-cpes-if-none']
if (subprocess.run(cmdline + ['dir:' + SW])).returncode != 0:
raise SystemExit('grype found problems during filesystem scan')
# Now test against the SBOM
import runpy
orig = sys.argv, sys.stdout
sys.argv = ['bypy', 'sbom', 'kovidgoyal/kitty', '1.0.0']
buf = io.StringIO()
sys.stdout = buf
runpy.run_path('bypy-src')
sys.argv, sys.stdout = orig
print(buf.getvalue())
if (subprocess.run(cmdline, input=buf.getvalue().encode())).returncode != 0:
raise SystemExit('grype found problems during SBOM scan')
def main() -> None:
if is_bundle:
setup_bundle_env()
@@ -324,23 +189,15 @@ def main() -> None:
if action == 'build':
build_kitty()
elif action == 'package':
build_kitty()
test_kitty()
package_kitty()
elif action == 'test':
test_kitty()
elif action == 'govulncheck':
subprocess.check_call(['go', 'install', 'golang.org/x/vuln/cmd/govulncheck@latest'])
subprocess.check_call(['govulncheck', '-mode=binary', 'kitty/launcher/kitten'])
subprocess.check_call(['govulncheck', './...'])
elif action == 'gofmt':
q = subprocess.check_output('gofmt -s -l tools kittens'.split()).decode()
if q.strip():
q = '\n'.join(filter(lambda x: not x.rstrip().endswith('_generated.go'), q.strip().splitlines())).strip()
if q:
raise SystemExit(q)
elif action == 'check-dependencies':
check_dependencies()
else:
raise SystemExit(f'Unknown action: {action}')

View File

@@ -23,15 +23,15 @@ jobs:
cc: [gcc, clang]
include:
- python: a
pyver: "3.11"
pyver: "3.10"
sanitize: 0
- python: b
pyver: "3.12"
pyver: "3.11"
sanitize: 1
- python: c
pyver: "3.13"
pyver: "3.12"
sanitize: 1
@@ -45,18 +45,18 @@ jobs:
steps:
- name: Checkout source code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v3
with:
fetch-depth: 10
persist-credentials: false
- name: Set up Python ${{ matrix.pyver }}
uses: actions/setup-python@v6
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.pyver }}
- name: Install Go
uses: actions/setup-go@v6
uses: actions/setup-go@v4
with:
go-version-file: go.mod
@@ -73,7 +73,7 @@ jobs:
CFLAGS: -funsigned-char
steps:
- name: Checkout source code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v3
with:
fetch-depth: 0 # needed for :commit: docs role
persist-credentials: false
@@ -85,18 +85,18 @@ jobs:
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
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v4
with:
python-version: "3.14"
python-version: "3.13"
- name: Install Go
uses: actions/setup-go@v6
uses: actions/setup-go@v4
with:
go-version-file: go.mod
cache: false
- name: Cache Go build artifacts separately
uses: actions/cache@v5
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
@@ -106,7 +106,7 @@ jobs:
${{ runner.os }}-golang-static-
- name: Install build-only deps
run: python -m pip install -r docs/requirements.txt ruff mypy types-requests types-docutils types-Pygments
run: python -m pip install -r docs/requirements.txt ruff mypy types-requests types-docutils
- name: Run ruff
run: ruff check .
@@ -117,12 +117,21 @@ jobs:
- name: Build kitty package
run: python .github/workflows/ci.py package
- name: Build kitty
run: python setup.py build --debug
- name: Run mypy
run: which python && python -m mypy --version && ./test.py mypy
- name: Run go vet
run: go version && go vet -tags testing ./...
- name: Build man page
run: make FAIL_WARN=1 man
- name: Build HTML docs
run: make FAIL_WARN=1 html
- name: Build static kittens
run: python setup.py build-static-binaries
@@ -134,16 +143,15 @@ jobs:
os: [ubuntu-latest, macos-latest]
env:
KITTY_BUNDLE: 1
KITTY_SANITIZE: 1
steps:
- name: Checkout source code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v3
with:
fetch-depth: 10
persist-credentials: false
- name: Install Go
uses: actions/setup-go@v6
uses: actions/setup-go@v4
with:
go-version-file: go.mod
@@ -158,24 +166,33 @@ jobs:
runs-on: macos-latest
steps:
- name: Checkout source code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v3
with:
fetch-depth: 0 # needed for :commit: docs role
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v4
with:
python-version: "3.14"
python-version: "3.11"
- name: Install Go
uses: actions/setup-go@v6
uses: actions/setup-go@v4
with:
go-version-file: go.mod
- name: Build kitty
run: python3 .github/workflows/ci.py build
- name: Test kitty
run: python3 .github/workflows/ci.py test
- name: Install deps for docs
run: python3 -m pip install -r docs/requirements.txt
- name: Builds docs
run: make FAIL_WARN=1 docs
- name: Build kitty package
run: python3 .github/workflows/ci.py package
@@ -187,7 +204,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v6.0.3
uses: actions/checkout@v3
with:
fetch-depth: 10
persist-credentials: false
@@ -196,7 +213,7 @@ jobs:
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
- name: Install Go
uses: actions/setup-go@v6
uses: actions/setup-go@v4
with:
go-version-file: go.mod

View File

@@ -1,14 +1,17 @@
name: "CodeQL"
name: "Code scanning - action"
on:
push:
branches: [master]
branches: [master, ]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
schedule:
- cron: '0 22 * * 5'
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
CodeQL-Build:
@@ -16,56 +19,30 @@ jobs:
contents: read # to fetch code (actions/checkout)
security-events: write # to upload SARIF results (github/codeql-action/analyze)
strategy:
fail-fast: false
matrix:
include:
- language: python
os: ubuntu-latest
- language: c
os: ubuntu-latest
- language: c
os: macos-latest
- language: go
os: ubuntu-latest
- language: actions
os: ubuntu-latest
runs-on: ${{ matrix.os }}
env:
KITTY_BUNDLE: 1
KITTY_CODEQL: 1
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.3
uses: actions/checkout@v3
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
persist-credentials: false
- name: Install Go
if: matrix.language == 'c' || matrix.language == 'go'
uses: actions/setup-go@v6
uses: actions/setup-go@v3
with:
go-version-file: go.mod
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4.36.2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
trap-caching: false
languages: python, c, go
- name: Build kitty
if: matrix.language == 'c' || matrix.language == 'go'
run: python3 .github/workflows/ci.py build
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4.36.2
- name: Run govulncheck
if: matrix.language == 'go'
run: python3 .github/workflows/ci.py govulncheck
uses: github/codeql-action/analyze@v3

View File

@@ -1,39 +0,0 @@
name: Depscan
on:
push:
branches: [master]
schedule:
- cron: '0 12 * * 5'
env:
CI: 'true'
ASAN_OPTIONS: detect_leaks=0
LC_ALL: en_US.UTF-8
LANG: en_US.UTF-8
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
dependecy-scanner:
name: Scan dependencies for vulnerabilities
runs-on: ubuntu-latest
env:
KITTY_BUNDLE: 1
steps:
- name: Checkout source code
uses: actions/checkout@v6.0.3
with:
fetch-depth: 10
persist-credentials: false
- name: Checkout bypy
uses: actions/checkout@v6.0.3
with:
fetch-depth: 1
persist-credentials: false
repository: kovidgoyal/bypy
path: bypy-src
- name: Check dependencies
run: python3 .github/workflows/ci.py check-dependencies

View File

@@ -2,99 +2,17 @@
# License: GPLv3 Copyright: 2024, Kovid Goyal <kovid at kovidgoyal.net>
import json
import os
import posixpath
import re
import subprocess
import sys
from collections import defaultdict, namedtuple
from collections import namedtuple
from datetime import datetime
from enum import Enum
from functools import cached_property
from typing import IO, Mapping, Optional
from typing import IO, List, Mapping, Optional
Frame = namedtuple('Frame', 'image_name image_base image_offset symbol symbol_offset')
Register = namedtuple('Register', 'name value')
# Cache mapping filename (basename) -> absolute path, built once on first use.
_build_file_cache: Optional[dict[str, str]] = None
def _build_filename_cache() -> dict[str, str]:
"""Walk the repo build tree and map each filename to its absolute path.
The script lives at <repo>/.github/workflows/, so the repo root is two
levels up. We scan the whole repo tree so we find both in-tree build
artefacts (e.g. kitty/fast_data_types.so) and those under build/.
"""
cache: dict[str, str] = {}
repo_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
for dirpath, _dirnames, filenames in os.walk(repo_root):
for fname in filenames:
# Keep the first occurrence (shallowest path wins).
if fname not in cache:
cache[fname] = os.path.join(dirpath, fname)
return cache
def _resolve_image_path(image_path: str) -> str:
"""Return the real filesystem path for *image_path*.
Crash reports on macOS redact parts of paths for privacy, replacing
directory components with ``*`` (e.g. ``/Users/USER/*/fast_data_types.so``).
These are *not* glob patterns the ``*`` is a literal privacy placeholder.
When such a path is detected, look up the basename in the build-file cache.
"""
if '*' not in image_path and '?' not in image_path:
return image_path
global _build_file_cache
if _build_file_cache is None:
_build_file_cache = _build_filename_cache()
basename = os.path.basename(image_path)
return _build_file_cache.get(basename, image_path)
def _get_source_locations(frames: list[Frame]) -> dict[int, str]:
"""Use atos to look up source file and line number for each frame.
Returns a mapping of frame index -> 'source_file:line_number' string.
Only frames with a known image path and base address are processed.
"""
# Group frames by (image_path, load_address) so we can batch atos calls.
by_image: dict[tuple[str, int], list[tuple[int, int]]] = defaultdict(list) # (path, base) -> [(address, frame_idx)]
for i, frame in enumerate(frames):
if frame.image_name and frame.image_base is not None and frame.image_offset is not None:
addr = frame.image_base + frame.image_offset
by_image[(frame.image_name, frame.image_base)].append((addr, i))
result: dict[int, str] = {}
for (image_path, load_addr), addr_frame_pairs in by_image.items():
addresses = [addr for addr, _ in addr_frame_pairs]
frame_indices = [idx for _, idx in addr_frame_pairs]
# Paths in crash reports may have privacy-redacted components (e.g.
# /Users/USER/*/fast_data_types.so). Resolve to a real path using the
# cached build-file index before passing to atos.
resolved_path = _resolve_image_path(image_path)
try:
cmd = ['atos', '-o', resolved_path, '-l', hex(load_addr)] + [hex(a) for a in addresses]
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
if proc.returncode == 0:
lines = proc.stdout.splitlines()
for frame_idx, line in zip(frame_indices, lines):
# atos output: "func_name (in binary) (source_file:line)"
# Extract the trailing "(file:line)" part. Use [^:]+ for the
# file portion since colons in filenames are rare/invalid on
# macOS, and this avoids false-matches with parentheses in paths.
m = re.search(r'\(([^:()]+:\d+)\)\s*$', line)
if m:
result[frame_idx] = m.group(1)
except (OSError, subprocess.SubprocessError):
pass
return result
def surround(x: str, start: int, end: int) -> str:
if sys.stdout.isatty():
@@ -351,7 +269,7 @@ class UserModeCrashReport(CrashReportBase):
return int(self._parse_field('Triggered by Thread'))
@cached_property
def frames(self) -> list[Frame]:
def frames(self) -> List[Frame]:
result = []
if self._is_json:
thread_index = self.faulting_thread
@@ -386,7 +304,7 @@ class UserModeCrashReport(CrashReportBase):
return result
@cached_property
def registers(self) -> list[Register]:
def registers(self) -> List[Register]:
result = []
if self._is_json:
thread_index = self._data['faultingThread']
@@ -490,9 +408,8 @@ class UserModeCrashReport(CrashReportBase):
result += '\n\n'
source_locations = _get_source_locations(self.frames)
result += bold('Frames:\n')
for i, frame in enumerate(self.frames):
for frame in self.frames:
image_base = '_HEADER'
if frame.image_base is not None:
image_base = f'0x{frame.image_base:x}'
@@ -501,14 +418,12 @@ class UserModeCrashReport(CrashReportBase):
result += f' + 0x{frame.image_offset:x}'
if frame.symbol is not None:
result += f' ({frame.symbol} + 0x{frame.symbol_offset:x})'
if i in source_locations:
result += f' [{source_locations[i]}]'
result += '\n'
return result
def get_crash_report_from_file(crash_report_file: IO[str]) -> CrashReportBase:
def get_crash_report_from_file(crash_report_file: IO) -> CrashReportBase:
metadata = json.loads(crash_report_file.readline())
try:

3
.gitignore vendored
View File

@@ -13,7 +13,6 @@
/tags
/build/
/fonts/
/shaders/
/linux-package/
/kitty.app/
/glad/out/
@@ -30,5 +29,3 @@ __pycache__/
.cache
bypy/b
bypy/virtual-machines.conf
_codeql_detected_source_root

1
.ignore Normal file
View File

@@ -0,0 +1 @@
kittens/unicode_input/names.h

View File

@@ -1,7 +1,5 @@
= kitty - the fast, feature-rich, cross-platform, GPU based terminal
If you live in the terminal, *kitty* is made for **you**!
See https://sw.kovidgoyal.net/kitty/[the kitty website].
image:https://github.com/kovidgoyal/kitty/workflows/CI/badge.svg["Build status", link="https://github.com/kovidgoyal/kitty/actions?query=workflow%3ACI"]

View File

@@ -7,12 +7,6 @@ and released just like all other bugs.
## Reporting a vulnerability
Preferably send an email to kovid at kovidgoyal.net or open a private security
advisory using the GitHub security advisory facility.
Note that I will generally respond to security communication within 72 hours. Once
the bug is confirmed, it will be fixed or at least mitigated within another 72
hours, at which time the fix will typically be committed to master and hence be
public. That timeline might be extended based on the severity of the issue and the
current state of master in terms of making a new release, if so, it will be
done in consultation with the issue reporter.
Preferably send an email to kovid at kovidgoyal.net or open an issue in the
GitHub repository, though the latter means you are disclosing the vulnerability
publicly before it can be fixed.

View File

@@ -50,7 +50,7 @@ def run_parsing_benchmark(cell_width: int = 10, cell_height: int = 20, scrollbac
screen = Screen(None, rows, columns, scrollback, cell_width, cell_height, 0, ToChild())
def parse_bytes(data: bytes|memoryview) -> None:
def parse_bytes(data: bytes) -> None:
data = memoryview(data)
while data:
dest = screen.test_create_write_buffer()

View File

@@ -96,8 +96,8 @@ func get_dependencies(path string) (ans []dependency) {
for _, line := range lines("otool", "-L", path) {
line = strings.TrimSpace(line)
if strings.Contains(line, "compatibility") && !strings.HasSuffix(line, ":") {
before, _, _ := strings.Cut(line, "(")
dep := strings.TrimSpace(before)
idx := strings.IndexByte(line, '(')
dep := strings.TrimSpace(line[:idx])
ans = append(ans, dependency{path: dep, is_id: dep == install_name})
}
}
@@ -279,12 +279,8 @@ func dependencies(args []string) {
which = "macos"
case "linux":
which = "linux"
switch runtime.GOARCH {
case "amd64":
case "arm64", "arm64be":
url = strings.Replace(url, "-64.", "-arm64.", 1)
default:
exit(fmt.Sprintf("Pre-built binaries are not available for the %s architecture", runtime.GOARCH))
if runtime.GOARCH != "amd64" {
exit("Pre-built dependencies are only available for the amd64 CPU architecture")
}
}
if which == "" {
@@ -393,7 +389,6 @@ func build(args []string) {
}
root := root_dir()
os.Setenv("DEVELOP_ROOT", root)
os.Setenv("SLANGC", filepath.Join(root, "bin", "slangc"))
prepend("PKG_CONFIG_PATH", filepath.Join(root, "lib", "pkgconfig"))
if runtime.GOOS == "darwin" {
os.Setenv("PKGCONFIG_EXE", filepath.Join(root, "bin", "pkg-config"))

View File

@@ -11,7 +11,7 @@ import sys
import tempfile
from contextlib import suppress
from bypy.constants import BIN, LIBDIR, PREFIX, PYTHON, ismacos, worker_env
from bypy.constants import LIBDIR, PREFIX, PYTHON, ismacos, worker_env
from bypy.constants import SRC as KITTY_DIR
from bypy.utils import run_shell, walk
@@ -39,7 +39,6 @@ def run(*args, **extra_env):
env.update(extra_env)
env['SW'] = PREFIX
env['LD_LIBRARY_PATH'] = LIBDIR
env['SLANGC'] = os.path.join(BIN, 'slangc')
if ismacos:
env['PKGCONFIG_EXE'] = os.path.join(PREFIX, 'bin', 'pkg-config')
cwd = env.pop('cwd', KITTY_DIR)
@@ -90,7 +89,7 @@ def build_frozen_tools(kitty_exe):
def sanitize_source_folder(path: str) -> None:
for q in walk(path):
if os.path.splitext(q)[1] not in ('.py', '.glsl', '.slang', '.ttf', '.otf', '.json'):
if os.path.splitext(q)[1] not in ('.py', '.glsl', '.ttf', '.otf', '.json'):
os.unlink(q)
@@ -100,7 +99,6 @@ def build_c_extensions(ext_dir, args):
shutil.copytree(
KITTY_DIR, writeable_src_dir, symlinks=True,
ignore=shutil.ignore_patterns('b', 'build', 'dist', '*_commands.json', '*.o', '*.so', '*.dylib', '*.pyd'))
shutil.rmtree(os.path.join(writeable_src_dir, 'shaders'))
with suppress(FileNotFoundError):
os.unlink(os.path.join(writeable_src_dir, 'kitty', 'launcher', 'kitty'))

View File

@@ -1,3 +1,3 @@
image 'https://cloud-images.ubuntu.com/releases/jammy/release/ubuntu-22.04-server-cloudimg-{}.img'
image 'https://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-{}.img'
deps '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'

View File

@@ -10,7 +10,7 @@ import subprocess
import tarfile
import time
from bypy.constants import BIN, LIBDIR, OUTPUT_DIR, PREFIX, python_major_minor_version
from bypy.constants import OUTPUT_DIR, PREFIX, python_major_minor_version
from bypy.freeze import extract_extension_modules, freeze_python, path_to_freeze_dir
from bypy.utils import get_dll_path, mkdtemp, py_compile, walk
@@ -90,14 +90,6 @@ def copy_libs(env):
shutil.copy2(x, dest)
dest = os.path.join(dest, os.path.basename(x))
subprocess.check_call(['chrpath', '-d', dest])
# Copy slang
x = 'libslang-compiler.so'
shutil.copy2(os.path.join(LIBDIR, f'{x}.0.0.0.0'), env.lib_dir)
os.symlink(f'{x}.0.0.0.0', os.path.join(env.lib_dir, x))
for x in ('glsl-module', 'glslang'):
x = f'libslang-{x}-0.0.0.so'
shutil.copy2(os.path.join(LIBDIR, x), env.lib_dir)
shutil.copy2(os.path.join(BIN, 'slangc'), env.bin_dir)
def add_ca_certs(env):
@@ -146,6 +138,10 @@ def copy_python(env):
shutil.rmtree(env.py_dir)
def build_launcher(env):
iv['build_frozen_launcher']([path_to_freeze_dir(), env.obj_dir])
def is_elf(path):
with open(path, 'rb') as f:
return f.read(4) == b'\x7fELF'
@@ -232,7 +228,7 @@ def main():
env = Env(os.path.join(ext_dir, kitty_constants['appname']))
copy_libs(env)
copy_python(env)
iv['build_frozen_launcher']([path_to_freeze_dir(), env.obj_dir])
build_launcher(env)
files = find_binaries(env)
fix_permissions(files)
add_ca_certs(env)

View File

@@ -1,8 +1,8 @@
# Requires installation of XCode >= 10.3 and go 1.26 and Python 3 and
# python3 -m pip install certifi meson ninja
# Requires installation of XCode >= 10.3 and go 1.23 and Python 3 and
# python3 -m pip install certifi
vm_name 'macos-kitty'
root '/Users/Shared/kitty-build'
python '/usr/local/bin/python3'
universal 'true'
deploy_target '12.0'
deploy_target '11.0'

View File

@@ -12,7 +12,7 @@ import sys
import tempfile
import zipfile
from bypy.constants import BIN, LIBDIR, PREFIX, PYTHON, SW, python_major_minor_version
from bypy.constants import PREFIX, PYTHON, SW, python_major_minor_version
from bypy.freeze import extract_extension_modules, freeze_python, path_to_freeze_dir
from bypy.macos_sign import codesign, create_entitlements_file, make_certificate_useable, notarize_app, verify_signature
from bypy.utils import current_dir, mkdtemp, py_compile, run_shell, timeit, walk
@@ -301,7 +301,7 @@ class Freeze(object):
self.fix_dependencies_in_lib(join(self.frameworks_dir, basename(path)))
@flush
def add_misc_libraries(self) -> None:
def add_misc_libraries(self):
for x in (
'sqlite3.0',
'z.1',
@@ -319,22 +319,6 @@ class Freeze(object):
dest = join(self.frameworks_dir, x)
self.set_id(dest, f'{self.FID}/{x}')
self.fix_dependencies_in_lib(dest)
# Copy slang
x = 'libslang-compiler.0.0.0.0.dylib'
shutil.copy2(os.path.join(LIBDIR, x), self.frameworks_dir)
os.symlink(x, os.path.join(self.frameworks_dir, 'libslang-compiler.dylib'))
dest = join(self.frameworks_dir, x)
self.set_id(dest, f'{self.FID}/{x}')
self.fix_dependencies_in_lib(dest)
for x in ('glsl-module', 'glslang'):
x = f'libslang-{x}-0.0.0.dylib'
shutil.copy2(os.path.join(LIBDIR, x), self.frameworks_dir)
dest = join(self.frameworks_dir, x)
self.set_id(dest, f'{self.FID}/{x}')
dest = os.path.join(self.contents_dir, 'MacOS')
shutil.copy2(os.path.join(BIN, 'slangc'), dest)
dest = os.path.join(dest, 'slangc')
self.fix_dependencies_in_lib(dest)
@flush
def add_package_dir(self, x, dest=None):

View File

@@ -1,310 +1,321 @@
[
{
"name": "zlib 1.3.2",
"name": "zlib",
"unix": {
"file_extension": "tar.xz",
"hash": "sha256:d7a0654783a4da529d1bb793b7ad9c3318020af77667bcae35f95d0e42a792f3",
"filename": "zlib-1.3.1.tar.xz",
"hash": "sha256:38ef96b8dfe510d42707d9c781877914792541133e1870841463bfa73f883e32",
"urls": ["https://zlib.net/{filename}"]
}
},
{
"name": "bzip2 1.0.8",
"name": "bzip2",
"os": "linux",
"unix": {
"file_extension": "tar.gz",
"filename": "bzip2-1.0.8.tar.gz",
"hash": "sha256:ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269",
"urls": ["https://www.sourceware.org/pub/{name}/{filename}"]
"urls": ["https://www.sourceware.org/pub/bzip2/bzip2-latest.tar.gz"]
}
},
{
"name": "pkg-config 0.29.2",
"type": "build",
"name": "pkg-config",
"os": "macos",
"unix": {
"file_extension": "tar.gz",
"filename": "pkg-config-0.29.2.tar.gz",
"hash": "sha256:6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591",
"urls": ["https://pkg-config.freedesktop.org/releases/{filename}"]
}
},
{
"name": "openssl 3.5.7",
"name": "openssl",
"unix": {
"file_extension": "tar.gz",
"hash": "sha256:a8c0d28a529ca480f9f36cf5792e2cd21984552a3c8e4aa11a24aa31aeac98e8",
"filename": "openssl-3.3.0.tar.gz",
"hash": "sha256:53e66b043322a606abf0087e7699a0e033a37fa13feb9742df35c3a33b18fb02",
"urls": ["https://www.openssl.org/source/{filename}"]
}
},
{
"name": "cmake 3.29.3",
"type": "build",
"name": "cmake",
"os": "macos",
"unix": {
"file_extension": "tar.gz",
"filename": "cmake-3.29.3.tar.gz",
"hash": "sha256:252aee1448d49caa04954fd5e27d189dd51570557313e7b281636716a238bccb",
"urls": ["https://cmake.org/files/v{version_except_last}/{filename}"]
"urls": ["https://cmake.org/files/v3.19/{filename}"]
}
},
{
"name": "expat 2.6.2",
"name": "expat",
"unix": {
"file_extension": "tar.xz",
"filename": "expat-2.6.2.tar.xz",
"hash": "sha256:ee14b4c5d8908b1bec37ad937607eab183d4d9806a08adee472c3c3121d27364",
"urls": ["https://github.com/libexpat/libexpat/releases/download/R_{version_with_underscores}/{filename}"]
"urls": ["https://github.com/libexpat/libexpat/releases/download/R_2_6_2/{filename}"]
}
},
{
"name": "libxml2 2.15.3",
"name": "libxml2",
"unix": {
"file_extension": "tar.xz",
"hash": "sha256:78262a6e7ac170d6528ebfe2efccdf220191a5af6a6cd61ea4a9a9a5042c7a07",
"urls": ["https://download.gnome.org/sources/libxml2/{version_except_last}/{filename}"]
"filename": "libxml2-2.12.7.tar.xz",
"hash": "sha256:24ae78ff1363a973e6d8beba941a7945da2ac056e19b53956aeb6927fd6cfb56",
"urls": ["https://download.gnome.org/sources/libxml2/2.12/{filename}"]
}
},
{
"name": "xkbcommon 1.7.0",
"name": "xkbcommon",
"os": "linux",
"unix": {
"file_extension": "tar.xz",
"filename": "libxkbcommon-1.7.0.tar.xz",
"hash": "sha256:65782f0a10a4b455af9c6baab7040e2f537520caa2ec2092805cdfd36863b247",
"urls": ["https://xkbcommon.org/download/lib{filename}"]
"urls": ["https://xkbcommon.org/download/{filename}"]
}
},
{
"name": "sqlite 3.53.2",
"name": "sqlite",
"unix": {
"file_extension": "tar.gz",
"hash": "sha256:588ad51949419a56ebe81fe56193d510c559eb94c9a57748387860b5d3069316",
"urls": ["https://www.sqlite.org/2026/{name}-autoconf-3530200.{file_extension}"]
"filename": "sqlite-autoconf-3450300.tar.gz",
"hash": "sha256:b2809ca53124c19c60f42bf627736eae011afdcc205bb48270a5ee9a38191531",
"urls": ["https://www.sqlite.org/2024/{filename}"]
}
},
{
"name": "libffi 3.4.6",
"name": "libffi",
"os": "linux",
"unix": {
"file_extension": "tar.gz",
"filename": "libffi-3.4.6.tar.gz",
"hash": "sha256:b0dea9df23c863a7a50e825440f3ebffabd65df1497108e5d437747843895a4e",
"urls": ["https://github.com/libffi/libffi/releases/download/v{version}/{filename}"]
"urls": ["https://github.com/libffi/libffi/releases/download/v3.4.6/{filename}"]
}
},
{
"name": "ncurses 6.5",
"name": "ncurses",
"os": "linux",
"unix": {
"file_extension": "tar.gz",
"filename": "ncurses-6.5.tar.gz",
"hash": "sha256:136d91bc269a9a5785e5f9e980bc76ab57428f604ce3e5a5a90cebc767971cc6",
"urls": ["https://ftp.gnu.org/gnu/ncurses/{filename}"]
}
},
{
"name": "readline 8.2",
"name": "readline",
"os": "linux",
"unix": {
"file_extension": "tar.gz",
"filename": "readline-8.2.tar.gz",
"hash": "sha256:3feb7171f16a84ee82ca18a36d7b9be109a52c04f492a053331d7d1095007c35",
"urls": ["https://ftp.gnu.org/gnu/readline/{filename}"]
}
},
{
"name": "xz 5.8.3",
"name": "xz",
"unix": {
"file_extension": "tar.gz",
"hash": "sha256:3d3a1b973af218114f4f889bbaa2f4c037deaae0c8e815eec381c3d546b974a0",
"filename": "xz-5.2.5.tar.gz",
"hash": "sha256:f6f4910fd033078738bd82bfba4f49219d03b17eb0794eb91efbae419f4aba10",
"urls": ["https://tukaani.org/xz/{filename}"]
}
},
{
"name": "libxxhash 0.8.2",
"name": "libxxhash",
"unix": {
"file_extension": "tar.gz",
"filename": "xxHash-0.8.2.tar.gz",
"hash": "sha256:baee0c6afd4f03165de7a4e67988d16f0f2b257b51d0e3cb91909302a26a79c4",
"urls": ["https://github.com/Cyan4973/xxHash/archive/refs/tags/v{version}.{file_extension}"]
"urls": ["https://github.com/Cyan4973/xxHash/archive/refs/tags/v0.8.2.tar.gz"]
}
},
{
"name": "xcrypt 4.4.36",
"name": "xcrypt",
"os": "linux",
"unix": {
"file_extension": "tar.gz",
"filename": "xcrypt-4.4.36.tar.gz",
"hash": "sha256:b979838d5f1f238869d467484793b72b8bca64c4eae696fdbba0a9e0b6c28453",
"urls": ["https://github.com/besser82/libxcrypt/archive/v{version}.{file_extension}"]
"urls": ["https://github.com/besser82/libxcrypt/archive/v4.4.36.tar.gz"]
}
},
{
"name": "python 3.14.6",
"name": "python",
"unix": {
"file_extension": "tar.xz",
"hash": "sha256:143b1dddefaec3bd2e21e3b839b34a2b7fb9842272883c576420d605e9f30c63",
"urls": ["https://www.python.org/ftp/python/{version}/Python-{version}.{file_extension}"]
"filename": "Python-3.12.3.tar.xz",
"hash": "sha256:56bfef1fdfc1221ce6720e43a661e3eb41785dd914ce99698d8c7896af4bdaa1",
"urls": ["https://www.python.org/ftp/python/3.12.3/{filename}"]
}
},
{
"name": "libpng 1.6.57",
"name": "libpng",
"unix": {
"file_extension": "tar.xz",
"hash": "sha256:d10c20d7171569804cae8dfc13ba6dcd0662c41ed39d43d4d429314aafb10a80",
"filename": "libpng-1.6.43.tar.xz",
"hash": "sha256:6a5ca0652392a2d7c9db2ae5b40210843c0bbc081cbd410825ab00cc59f14a6c",
"urls": ["https://downloads.sourceforge.net/sourceforge/libpng/{filename}"]
}
},
{
"name": "lcms2 2.17",
"name": "lcms2",
"unix": {
"file_extension": "tar.gz",
"hash": "sha256:d11af569e42a1baa1650d20ad61d12e41af4fead4aa7964a01f93b08b53ab074",
"urls": ["https://github.com/mm2/Little-CMS/releases/download/lcms{version}/lcms2-{version}.{file_extension}"]
"filename": "lcms2-2.16.tar.gz",
"hash": "sha256:d873d34ad8b9b4cea010631f1a6228d2087475e4dc5e763eb81acc23d9d45a51",
"urls": ["https://github.com/mm2/Little-CMS/archive/2.16/{filename}"]
}
},
{
"name": "graphite 1.3.14",
"name": "graphite",
"os": "linux",
"unix": {
"file_extension": "tgz",
"filename": "graphite2-1.3.14.tgz",
"hash": "sha256:f99d1c13aa5fa296898a181dff9b82fb25f6cc0933dbaa7a475d8109bd54209d",
"urls": ["https://downloads.sourceforge.net/silgraphite/graphite2-{version}.{file_extension}"]
"urls": ["https://downloads.sourceforge.net/silgraphite/{filename}"]
}
},
{
"name": "pcre 10.43",
"name": "pcre",
"os": "linux",
"unix": {
"file_extension": "tar.bz2",
"filename": "pcre2-10.43.tar.bz2",
"hash": "sha256:e2a53984ff0b07dfdb5ae4486bbb9b21cca8e7df2434096cc9bf1b728c350bcb",
"urls": ["https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.43/pcre2-{version}.{file_extension}"]
"urls": ["https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.43/{filename}"]
}
},
{
"name": "iconv 1.17",
"name": "iconv",
"os": "linux",
"unix": {
"file_extension": "tar.gz",
"filename": "libiconv-1.17.tar.gz",
"hash": "sha256:8f74213b56238c85a50a5329f77e06198771e70dd9a739779f4c02f65d971313",
"urls": ["https://ftp.gnu.org/pub/gnu/libiconv/libiconv-{version}.{file_extension}"]
"urls": ["https://ftp.gnu.org/pub/gnu/libiconv/{filename}"]
}
},
{
"name": "glib 2.86.3",
"os": "linux",
"name": "installer",
"comment": "Needed infrastructure for installing pure python packages (wheels)",
"unix": {
"file_extension": "tar.xz",
"hash": "sha256:b3211d8d34b9df5dca05787ef0ad5d7ca75dec998b970e1aab0001d229977c65",
"urls": ["https://download.gnome.org/sources/glib/{version_except_last}/{filename}"]
"filename": "installer-0.7.0-py3-none-any.whl",
"hash": "sha256:05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53",
"urls": ["pypi"]
}
},
{
"name": "libbrotli 1.1.0",
"name": "packaging",
"comment": "Needed for glib for some absurd reason",
"unix": {
"filename": "packaging-23.1-py3-none-any.whl",
"hash": "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61",
"urls": ["pypi"]
}
},
{
"name": "glib",
"os": "linux",
"unix": {
"file_extension": "tar.gz",
"filename": "glib-2.80.2.tar.xz",
"hash": "sha256:b9cfb6f7a5bd5b31238fd5d56df226b2dda5ea37611475bf89f6a0f9400fe8bd",
"urls": ["https://download.gnome.org/sources/glib/2.80/{filename}"]
}
},
{
"name": "brotli",
"os": "linux",
"unix": {
"filename": "brotli-1.1.0.tar.gz",
"hash": "sha256:e720a6ca29428b803f4ad165371771f5398faba397edf6778837a18599ea13ff",
"urls": ["https://github.com/google/brotli/archive/v{version}/{filename}"]
"urls": ["https://github.com/google/brotli/archive/v1.1.0/{filename}"]
}
},
{
"name": "pixman 0.44.2",
"name": "pixman",
"os": "linux",
"unix": {
"file_extension": "tar.xz",
"filename": "pixman-0.44.2.tar.xz",
"hash": "sha256:50baf820dde0c5ff9714d03d2df4970f606a3d3b1024f5404c0398a9821cc4b0",
"urls": ["https://www.cairographics.org/releases/{filename}"]
"urls": ["https://www.cairographics.org/releases/pixman-0.44.2.tar.xz"]
}
},
{
"name": "freetype 2.13.2",
"name": "freetype",
"os": "linux",
"unix": {
"file_extension": "tar.xz",
"filename": "freetype-2.13.2.tar.xz",
"hash": "sha256:12991c4e55c506dd7f9b765933e62fd2be2e06d421505d7950a132e4f1bb484d",
"urls": ["https://download.savannah.gnu.org/releases/freetype/{filename}"]
}
},
{
"name": "fontconfig 2.17.1",
"name": "fontconfig",
"os": "linux",
"unix": {
"file_extension": "tar.xz",
"hash": "sha256:9f5cae93f4fffc1fbc05ae99cdfc708cd60dfd6612ffc0512827025c026fa541",
"urls": ["https://gitlab.freedesktop.org/api/v4/projects/890/packages/generic/fontconfig/{version}/{filename}"]
"filename": "fontconfig-2.13.1.tar.bz2",
"hash": "sha256:f655dd2a986d7aa97e052261b36aa67b0a64989496361eca8d604e6414006741",
"urls": ["https://www.fontconfig.org/release/{filename}"]
}
},
{
"name": "cairo 1.18.2",
"name": "cairo",
"os": "linux",
"unix": {
"file_extension": "tar.xz",
"filename": "cairo-1.18.2.tar.xz",
"hash": "sha256:a62b9bb42425e844cc3d6ddde043ff39dbabedd1542eba57a2eb79f85889d45a",
"urls": ["https://www.cairographics.org/releases/{filename}"]
"urls": ["https://www.cairographics.org/releases/cairo-1.18.2.tar.xz"]
}
},
{
"name": "harfbuzz 12.3.0",
"name": "harfbuzz",
"unix": {
"file_extension": "tar.xz",
"hash": "sha256:8660ebd3c27d9407fc8433b5d172bafba5f0317cb0bb4339f28e5370c93d42b7",
"urls": ["https://github.com/harfbuzz/harfbuzz/releases/download/{version}/{filename}"]
"filename": "harfbuzz-8.5.0.tar.xz",
"hash": "sha256:77e4f7f98f3d86bf8788b53e6832fb96279956e1c3961988ea3d4b7ca41ddc27",
"urls": ["https://github.com/harfbuzz/harfbuzz/releases/download/8.5.0/{filename}"]
}
},
{
"name": "simde 0.8.2",
"name": "simde",
"comment": "Cannot update till gcc in the build VM is updated as simde 0.8 requires newer gcc",
"unix": {
"file_extension": "tar.xz",
"hash": "sha256:59068edc3420e75c5ff85ecfd80a77196fb3a151227a666cc20abb313a5360bf",
"urls": ["https://github.com/simd-everywhere/simde/releases/download/v{version}/simde-amalgamated-{version}.{file_extension}"]
"filename": "simde-amalgamated-0.7.6.tar.xz",
"hash": "sha256:703eac1f2af7de1f7e4aea2286130b98e1addcc0559426e78304c92e2b4eb5e1",
"urls": ["https://github.com/simd-everywhere/simde/releases/download/v0.7.6/{filename}"]
}
},
{
"name": "slang 2026.12.2",
"unix": {
"file_extension": "tar.gz",
"hash": "sha256:1ed5ebf7886849ea621d6dabee20248e8551c80134aa8c4433746879cb20dea3",
"urls": ["git-submodules://github.com/shader-slang/slang.git"]
}
},
{
"name": "wayland 1.24.0",
"name": "wayland",
"os": "linux",
"unix": {
"file_extension": "tar.xz",
"hash": "sha256:82892487a01ad67b334eca83b54317a7c86a03a89cfadacfef5211f11a5d0536",
"urls": ["https://gitlab.freedesktop.org/wayland/wayland/-/releases/{version}/downloads/{filename}"]
"filename": "wayland-1.23.0.tar.xz",
"hash": "sha256:05b3e1574d3e67626b5974f862f36b5b427c7ceeb965cb36a4e6c2d342e45ab2",
"urls": ["https://gitlab.freedesktop.org/wayland/wayland/-/releases/1.23.0/downloads/{filename}"]
}
},
{
"name": "wayland-protocols 1.45",
"name": "wayland-protocols",
"os": "linux",
"unix": {
"file_extension": "tar.xz",
"hash": "sha256:4d2b2a9e3e099d017dc8107bf1c334d27bb87d9e4aff19a0c8d856d17cd41ef0",
"urls": ["https://gitlab.freedesktop.org/wayland/wayland-protocols/-/releases/{version}/downloads/{filename}"]
"filename": "wayland-protocols-1.41.tar.xz",
"hash": "sha256:2786b6b1b79965e313f2c289c12075b9ed700d41844810c51afda10ee329576b",
"urls": ["https://gitlab.freedesktop.org/wayland/wayland-protocols/-/releases/1.41/downloads/{filename}"]
}
}
]

View File

@@ -13,6 +13,6 @@ for attr in ('linguist-generated', 'linguist-vendored'):
fname = line.split(':', 1)[0]
all_files.discard(fname)
all_files -= {'gen/rowcolumn-diacritics.txt'}
all_files -= {'gen/nerd-fonts-glyphs.txt', 'gen/rowcolumn-diacritics.txt'}
cp = subprocess.run(['cloc', '--list-file', '-'], input='\n'.join(all_files).encode())
raise SystemExit(cp.returncode)

View File

@@ -8,7 +8,4 @@ using the ``map`` and ``mouse_map`` directives in :file:`kitty.conf`. For
configuration examples, see the default shortcut links for each action.
To read about keyboard mapping in more detail, see :doc:`mapping`.
You can also browse and trigger these actions by pressing :sc:`command_palette`
to run the :doc:`/kittens/command-palette`.
.. include:: /generated/actions.rst

View File

@@ -25,7 +25,6 @@ Previous shell prompt :sc:`scroll_to_previous_prompt` (see :ref:`shell_int
Next shell prompt :sc:`scroll_to_next_prompt` (see :ref:`shell_integration`)
Browse scrollback in less :sc:`show_scrollback`
Browse last cmd output :sc:`show_last_command_output` (see :ref:`shell_integration`)
Search scrollback in less :sc:`search_scrollback` (also :kbd:`⌘+F` on macOS)
========================= =======================
The scroll actions only take effect when the terminal is in the main screen.
@@ -41,7 +40,7 @@ Action Shortcut
======================== =======================
New tab :sc:`new_tab` (also :kbd:`⌘+t` on macOS)
Close tab :sc:`close_tab` (also :kbd:`⌘+w` on macOS)
Next tab :sc:`next_tab` (also :kbd:`⌃+⇥` and :kbd:`⇧+⌘+]` on macOS)
Next tab :sc:`next_tab` (also :kbd:`⇧+⌃+⇥` and :kbd:`⇧+⌘+]` on macOS)
Previous tab :sc:`previous_tab` (also :kbd:`⇧+⌃+⇥` and :kbd:`⇧+⌘+[` on macOS)
Next layout :sc:`next_layout`
Move tab forward :sc:`move_tab_forward`
@@ -117,9 +116,6 @@ Similarly, you can detach the current tab, with::
# asks which OS Window to move the tab into
map ctrl+f4 detach_tab ask
Note that tabs can be re-arranged, detached and moved to another OS Window in
the same kitty instance using drag and drop.
Finally, you can define a shortcut to close all windows in a tab other than the
currently active window::

View File

@@ -82,14 +82,13 @@ These dependencies are needed when building against system libraries only.
Run-time dependencies:
* ``python``
* ``python`` >= 3.8
* ``harfbuzz`` >= 2.2.0
* ``zlib``
* ``libpng``
* ``liblcms2``
* ``libxxhash``
* ``openssl``
* ``shader-slang`` (for the slangc compiler)
* ``pixman`` (not needed on macOS)
* ``cairo`` (not needed on macOS)
* ``freetype`` (not needed on macOS)

View File

@@ -9,79 +9,6 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
Recent major new features
---------------------------
Vertical tabs [0.48]
~~~~~~~~~~~~~~~~~~~~~~~~~~
kitty now has support for :pull:`vertical tabs <9855>` along the left or right edge of the OS
Window. Useful for people that have wide aspect ratio windows.
Drag and drop for terminal programs [0.47]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
kitty now has a new :doc:`/kittens/dnd` kitten that allows you to seamlessly
drag and drop files between kitty and any GUI program like the OS file manager
or a webapp. It even works over SSH! It is powered by a new :doc:`protocol
</dnd-protocol>` allowing the use of drag and drop from any TUI program.
Mousing [0.46]
~~~~~~~~~~~~~~~
kitty already had excellent mouse support, but now it is taking it to the next
level. The kitty scrollback buffer grew support for :opt:`smooth scrolling
<pixel_scroll>` and :opt:`momentum based scrolling <momentum_scroll>`
for a natural, smooth and kinetic scrolling experience.
Additionally, you can now :opt:`drag kitty tabs around <drag_threshold>` with the mouse
to re-order them, move them to another kitty OS Window or even detach them into
their own OS Window.
Finally, a long requested feature, the ability to resize kitty windows (aka
splits) with the mouse was implemented.
Choose files, fast [0.45]
~~~~~~~~~~~~~~~~~~~~~~~~~~~
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. Allows you to select files for use at the shell prompt or other
terminal workflows with just a few keystrokes, similar to how fuzzy finders
like `fzf <https://github.com/junegunn/fzf/>`__ operate, but designed for
files in particular, leveraging the various innovations of kitty such as image
display and variable sized text.
On Linux, it can even be used as a :doc:`drop in replacement </kittens/desktop-ui>`
for the File Open/Save dialog boxes in GUI programs.
Sessions [0.43]
~~~~~~~~~~~~~~~~
kitty has long had support for :doc:`sessions`, aka simple text files where you
can define what tabs, windows and programs you wish to run in kitty. Now in
addition to that kitty has the ability to :ref:`create and switch between
sessions <goto_session>` with a single keypress and also to manually setup some
tabs/windows in kitty and :ref:`save it as a session file <complex_sessions>`,
for seamless and intuitive session file creation.
A scrollbar for the kitty scrollback [0.43]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A long requested feature, kitty has finally :pull:`gotten a scrollbar <8945>`
that can be used with the mouse for browsing its scrollback. The bar appear
automatically when you start scrolling backwards and is :opt:`extensively
configurable <scrollbar>` in kitty.conf. Note that the old ``scrollback_indicator_opacity``
option is deprecated.
Multiple cursors [0.43]
~~~~~~~~~~~~~~~~~~~~~~~~
kitty has pioneered a new :doc:`escape code protocol
<multiple-cursors-protocol>` that allows terminal applications to use multiple
cursors, rendered natively. These are typically used in editors to make the
same edit at multiple locations. Now terminal based editors can use properly
rendered native cursors, just like their GUI cousins, at last.
Access kitty with a single keypress [0.42]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -179,687 +106,12 @@ consumption to do the same tasks.
Detailed list of changes
-------------------------------------
0.48.0 [future]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Implement vertical tabs by setting :opt:`tab_bar_edge` to ``left`` or ``right`` (:pull:`9855`)
- Graphics protocol: Add a new :ref:`transient usage hint <image_usage_hints>` that clients can send to terminals to indicate an image is meant for only short duration use (:pull:`10092`)
- kitten @ get-text: Add support for :code:`alternate` and :code:`alternate_scrollback` extents to fetch text from the alternate screen buffer (:iss:`10165`)
- Wayland: Fix first OS window being a few cells too small when ``initial_window_width/initial_window_height`` are set in cells and a fractional display scale is in use (:iss:`10146`)
- kitty binary builds are now built on Ubuntu 22 upgraded from Ubuntu 18 for improved performance from better compilers
- macOS: Fix incorrect horizontal alignment when using text sizing protocol (:iss:`10179`)
- ``edit-in-kitty``: Return exit code from underlying editor process on exit (:iss:`10198`)
0.47.4 [2026-06-15]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Linux: Fix a regression in the previous release that broke rendering of bitmap color fonts (:pull:`10145`)
- Linux: Allow fake italics defined via a matrix in fontconfig settings to work for fonts like Fira Code that do not ship with an italic face (:pull:`10120`)
0.47.3 [2026-06-12]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- macOS: Show a key symbol on the active tab if the macOS Secure Input feature is enabled
- Fix regression that broke unserialization of splits layout in previous release (:iss:`10124`)
- Fix :opt:`focus_follows_mouse` switching the active window when returning to a desktop/space, even though the mouse did not move. Now the window under a stationary cursor is left alone, while moving the mouse across windows still switches focus as before.
- Sanitise responses to color control escape codes to avoid command injection for shells that do not use the kitty keyboard protocol (:cve:`2026-54057`)
- choose fonts kitten: Fix a rare timing based race causing kitten to crash at startup (:pull:`10128`)
- Wayland: Fix mouse input getting broken when starting a tab drag and releasing the mouse button before the drag is actually registered (:pull:`10136`)
0.47.2 [2026-06-07]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Allow dragging to move scrollbar after clicking on track when :opt:`scrollbar_jump_on_click` is enabled (:pull:`10085`)
- macOS: Fix regression in 0.47.0 that broke passing :kbd:`Cmd+C` on to terminal applications when no text is selected (:iss:`10087`)
- ``kitten @ set-background-image``: Fix ``--layout=configured`` changing layout to centered instead (:iss:`10089`)
- Splits layout: add an ``equalize`` action and an ``equalize_on_close`` option to redistribute split space proportionally (:iss:`3489`)
- Fix matching var/env on tabs not working as expected (:iss:`10095`)
- When watching for changed config files do not recursively watch all sub directories of the directory containing the config file (:iss:`10102`)
- File transfer protocol: use O_NOFOLLOW when opening regular files (:cve:`2026-54055`)
- dnd kitten: Protect against drops from malicious sources (:cve:`2026-54056`)
0.47.1 [2026-05-28]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Fix a regression in the previous release that caused :ac:`copy_or_noop` to stop working correctly (:pull:`10041`)
- macOS: Fix a regression in the previous release that caused URLs to be quoted when dropping into shells (:iss:`10054`)
- Fix a regression in the previous release that broke automatic color scheme changes when using a background image (:iss:`10058`)
- Fix :opt:`auto_reload_config` not working when :file:`kitty.conf` is a symlink (:iss:`10066`)
- Fix a regression in the previous release that broke dragging of URLs to the shell prompt from programs that dont support MOVE drag operations
- Preserve user-set tab stops across window resizes instead of resetting to 8 column default
- Add support for the DECST8C escape sequence (``CSI ? 5 W``) to reset tab stops to every 8 columns
- X11: Fix panel/quick-access-terminal windows not staying on top under KDE after they are hidden once (:iss:`10082`)
0.47.0 [2026-05-19]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- A new :doc:`Drag and drop kitten </kittens/dnd>` to allow drag and drop of files from your shell to any GUI program even across SSH (:iss:`9984`)
- A new option :opt:`palette_generate` to automatically generate the 256 color palette from the first 16 colors (:pull:`9426`)
- For builtin key mappings automatically :ref:`fallback <mapping-fallback>` to matching the US-PC layout key when the pressed key has no matches and is a non-English character (:pull:`9671`)
- Allow drag and drop of windows to re-arrange them, move them to another tab/OS Window or detach them into a new OS Window. See :ac:`toggle_window_title_bars` to temporarily show window title bars to drag them around (:pull:`9626`)
- Have :ac:`scroll_line_up` and :ac:`scroll_line_down` smooth scroll by default. Can be restored to old behavior by re-mapping without the ``smooth`` argument (:iss:`9689`)
- Draw a progress bar at the top of the window when a program reports progress using the OSC 9;4 escape sequence, controlled by :opt:`progress_bar` (:iss:`9777`)
- Automatically reload configuration on changes, controlled by :opt:`auto_reload_config`
- Allow specifying multiple background images for :opt:`background_image` that are stored on GPU to allow fast image switching (:pull:`9836`)
- :doc:`Remote control <remote-control>`: Expose :code:`session_name` and :code:`last_focused_at` in the output of ``kitten @ ls`` for each window (:iss:`9732`, :iss:`9799`)
- Allow optionally dragging URLs with the mouse, see :sc:`start_simple_selection` (:pull:`9804`)
- Change :opt:`focus_follows_mouse` to switch the active window only when the mouse crosses into a different window, instead of on every mouse motion event. Prevents accidental mouse bumps from undoing a keyboard-driven window switch.
- Wayland: Use hold gestures to cancel momentum scrolling when fingers are placed on the trackpad, for a more natural kinetic scrolling experience (:iss:`9863`)
- macOS: Switch to new Tahoe style application icon with different background in light and dark modes
- Fix thickness of diagonal lines in box drawing characters not the same as horizontal/vertical lines (:iss:`9719`)
- Graphics protocol: Fix crash when handling invalid PNG image with direct transmission (:cve:`2026-33633`)
- Graphics protocol: Fix crash when handling invalid offset values in graphics compose commands (:cve:`2026-33642`)
- X11: Fix a regression in the previous release that caused an occasional crash on input device removal (:iss:`9723`)
- XWayland: Fix a regression where some wheel mice were not scrolling properly (:pull:`9770`)
- Command palette: Improve searching to use word level matching (:pull:`9727`)
- hints kitten: A new option to set the background color of matched text (:pull:`9745`)
- The :opt:`show_hyperlink_targets` option now allows specifying a keyboard modifier so that target URLs are only shown on hover when the modifier is pressed (:pull:`9741`)
- Fix a spurious mouse button release event being sent when dragging out of an OS window causes focus loss
- Fix scrollbar hover/interaction not working when the scrollbar is drawn in the window margin area (:iss:`9756`)
- Fix completion for ``edit-in-kitty`` not including some common text file types
- Allow holding the :kbd:`Alt` key and triple-clicking to select from the first cell even if it is empty (:pull:`9758`)
- Fix double click to rename tab being triggered too easily (:iss:`9774`)
- Fix a crash when user tries to select while the client program is using synchronised rendering and generating large amounts of output (:iss:`9778`)
- macOS: Add Copy and Paste menu items to the Edit menu in the global menu bar (:iss:`9780`)
- Fix dragging of splits layout borders sometimes moving in the wrong direction or having no effect (:pull:`9447`)
- Fix triple-click line selection and double-click word selection not extending wrapped lines beyond the edges of the viewport
- Password input in kittens: hide the cursor and display a blinking 🔒 at the end of typed characters to make it visually clear the user is entering a password
- edit-in-kitty: Ignore environment variables as some editors execute code present in env vars. Similarly ignore conf file specifications for colors (:cve:`2026-42851`)
- Command palette :sc:`command_palette`: nicer grouping of aliases and combined actions (:pull:`9819`)
- hints kitten: Fix trailing punctuation not being removed from URLs (:pull:`9828`)
- Fix copy/paste dropping spaces at soft-wrap boundaries when :opt:`strip_trailing_spaces` is set (:iss:`9834`)
- Allow setting negative values for :opt:`inactive_text_alpha` to control
whether to only fade inactive windows or unfocused windows (:pull:`9837`)
- A new option :opt:`macos_fullscreen_ignore_safe_area_insets` to control
whether to ignore the notch space when using :opt:`macos_traditional_fullscreen` (:pull:`9841`)
- Fix some responses from terminal sometimes leaking into shell after kitten exit (:iss:`9839`)
- Render block elements from the Unicode Symbols for Legacy Computing Supplement block (U+1CC00U+1CEBF): separated block quadrants, separated block sextants, one sixteenth blocks, and one quarter block partial fills (:disc:`9849`)
- Improve performance of using active process data when rendering the tab bar by only scanning processes once per second (:iss:`9862`)
- macOS: Fix occasional phantom cursors being drawn on screen (:iss:`9725`)
- diff kitten: Keep the current (topmost) filename visible when scrolling, controlled by a new option :opt:`kitten-diff.sticky_header` (:pull:`9891`)
- Add an option to :opt:`focus_follows_mouse` to only switch focus on drops rather than movement (:pull:`9896`)
- Fix setting :opt:`momentum_scroll` to zero not *fully* disabling momentum scrolling (:iss:`9904`)
- macOS: Fix args passed via ``open --args`` being ignored when :file:`macos-launch-services-cmdline` is present (:iss:`9910`)
- :ac:`save_as_session`: when the filename input by the user has no extension, automatically add the ``.kitty-session`` extension (:pull:`9919`)
- Linux: Workaround bug in Nvidia drivers that caused color corruption when resuming after suspend (:iss:`9844`)
- choose-files kitten: Output a trailing newline when writing to a tty in text format (:iss:`9982`)
- ssh kitten: Sanitize user controlled data in error messages that might leak to shell (:cve:`2026-42850`)
- Linux: Respect the fontconfig matrix setting commonly used for fake slant with fonts that do not have italic variants (:pull:`9990`)
0.46.2 [2026-03-21]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Watchers: Add an `on_quit` event to global watchers (:iss:`9675`)
- Wayland: Fix a crash on some compositors when dragging a tab between OS
Windows (:iss:`9677`)
- Fix incorrect behavior when using the actions to move tab forward/backward
with a tab_bar_filter active (:iss:`9672`)
- Prevent stacking of multiple rename tab windows (:iss:`9691`)
- choose files kitten: Fix a regression that caused incorrect highlight of matched letters
- macOS: When using :opt:`macos_traditional_fullscreen` do not render content under the notch (:pull:`9678`)
- X11: Fix massive scroll when switching focus between kitty and another application (:iss:`9703`)
- Markers: Fix marking not working for multicell characters (:iss:`9705`)
- Fix a regression in 0.46 that broke drag select in unfocused windows
(:iss:`9713`)
0.46.1 [2026-03-16]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- diff kitten: Highlight moved lines using a different background color (:opt:`kitten-diff.mark_moved_lines`) (:iss:`3241`)
- Fix a regression that broke ``kitten update-self`` (:iss:`9642`)
- macOS: Clear bell alert badge on dock icon on mouse/keyboard activity (:iss:`9640`)
- Fix a regression that broke accept anyway shortcut in the paste confirmation dialog (:pull:`9640`)
- Fix kitty hanging on startup on Intel macs (:iss:`9643`)
- X11: Fix a regression that caused some high res scroll devices to be treated as line based scroll devices (:iss:`9649`)
- Wayland: Fix momentum scrolling not working on compositors that send a stop frame with no axis information (:iss:`9653`)
- Linux: Fix regression that broke drag and drop from GTK applications (:iss:`9656`)
- macOS: Fix using Fn key for start dictation not working (:iss:`9661`)
- Don't use neighboring tab colors for tab bar margins in translucent windows (:iss:`9663`)
- macOS: Fix OS window focus not restored when switching spaces (:iss:`9665`)
0.46.0 [2026-03-11]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Pixel scrolling for the kitty scrollback buffer controlled via :opt:`pixel_scroll` (:pull:`9330`)
- Linux: momentum scrolling in the kitty scrollback buffer for touchpads and touchscreens, see :opt:`momentum_scroll`
- X11: support high resolution scroll events from touchpads, etc
- macOS: Implement support for Apple dictation to input text in kitty (:iss:`3732`)
- Allow dragging tabs (opt:`drag_threshold`) in the tab bar to re-order, move to another OS Window or
detach (:pull:`9296`)
- Allow dragging window borders to resize kitty windows in all the different
layouts, controlled by :opt:`window_drag_tolerance` (:pull:`9447`)
- Allow showing :opt:`configurable window titles <window_title_bar>` for individual kitty
windows via a window title bar (:pull:`9450`)
- A command palette (:sc:`command_palette`) to browse and trigger all mapped and unmapped actions
(:pull:`9545`)
- choose-files kitten: Fix JXL image preview not working (:iss:`9323`)
- Fix tab bar rendering glitches when using :opt:`tab_bar_filter` in some
circumstances (:iss:`9328`)
- Add support for specifying colors in :file:`kitty.conf` in OKLCH and LAB
color spaces (:pull:`9325`)
- Fix a regression that broke using line numbers with the edit-in-kitty command
(:pull:`9346`)
- Key maps: Allow specifying a timeout for multi key mappings and keyboard modes (:pull:`9551`)
- macOS: Fix changes to :opt:`macos_titlebar_color` while in full screen not being applied after exiting fullscreen (:iss:`9350`)
- ncurses: Fix ncurses not using dim because it is missing from the sgr property
in terminfo even though it is present in the dim property.
- Fix a regression in the previous release that caused moving between neighbors
in the vertical and horizontal layouts to go in the opposite direction (:iss:`9355`)
- Fix :ac:`goto_session` not respecting the focus_tab session directive when
creating a session in an existing OS window (:iss:`9366`)
- Wayland: Fix a regression in the previous release that caused doubled key
repeats on compositors that implement compositor side key repeat events
(:iss:`9374`)
- icat: Fix a regression in the previous release when rendering GIF animations
with frames that dispose onto background with non-zero delay using the native
engine (:iss:`9376`)
- Wayland: Remove usage of the Wayland color management protocol to inform
compositors of the color space used by kitty (:iss:`9341`)
- Linux: Fix a regression in 0.40 that caused horizontal alignment for emoji to
be incorrect in some cases (:iss:`9395`)
- icat kitten: When catting multiple images display the images in input order (:iss:`9413`)
- kitten @: Fix relative paths for --password-file being resolved relative to
CWD instead of the kitty config directory
- kitten choose-files: Add a new binding of :kbd:`Alt+Enter` to modify the name
of an existing file when choosing a save file name (:iss:`9387`)
- kitten choose-files: Fix TAB completion in the choose save file name prompt
not working with respect to the current working directory (:iss:`9387`)
- Fix line-at-once selection not extending wrapped lines into scrollback (:iss:`9437`)
- ssh kitten: Restore keyboard mode even if the ssh connection drops
- edit-in-kitty: Handle connection drop more gracefully (:pull:`9480`)
- macOS: Fix changing window title with global menubar menu open causes menu to
get stuck (:pull:`9490`)
- Fix :opt:`focus_follows_mouse` not working during a drag and drop (:iss:`9497`)
- :ac:`goto_session`: Add a ``--active-only`` option to select from only active
sessions (:pull:`9503`)
- Shell integration: Allow sending click events to shells using y co-ordinates
relative to prompts (:iss:`9500`)
- A new action :ac:`copy_selection_or_last_command_output` (:pull:`9512`)
- Wayland: Add support for the background blur extension (:iss:`9534`)
- macOS: A new option :opt:`macos_dock_badge_on_bell` to show a badge on the
kitty dock icon when a bell occurs (:pull:`9529`)
- macOS: Workaround for yet another Tahoe bug causing rendering to fail
(:pull:`9520`)
- URL detection: Allow trailing asterisks in URLs (:iss:`9543`)
- Wayland: Add support for :code:`titlebar-only` in :opt:`hide_window_decorations`
to hide the titlebar while keeping shadows for window resizing. (:pull:`9486`)
- Text sizing protocol: Fix alignment/cropping issues when rendering text with
a fractional scale (:iss:`9471`)
- macOS: Fix a crash when using :opt:`macos_traditional_fullscreen` with split
view (:pull:`9573`)
- macOS: Fix flickering during OS Window resize (:disc:`9582`)
- Cursor trail: Show a cursor trail when switching tabs (:pull:`9588`)
- Make shift+left click extend the current selection instead of starting a new
selection when the mouse is not grabbed by the TUI application (:disc:`9608`)
- Allow double clicking on a tab to rename it (:pull:`9609`)
- :ac:`remote_control_script` resolve relative paths with respect to kitty
config directory (:iss:`9625`)
- Splits layout: Add new mappable actions to maximize a window in the splits
layout (:iss:`9629`)
0.45.0 [2025-12-24]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 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`)
- Add support for the `paste events protocol <https://rockorager.dev/misc/bracketed-paste-mime/>`__ (:iss:`9183`)
- icat kitten: Add support for animated PNG and animated WebP, netPBM images, ICC color profiles and CCIP color space metadata to the builtin engine
- icat kitten: Add a new flag :option:`kitty +kitten icat --fit` to control how images are scaled to fit the screen (:iss:`9201`)
- icat kitten: The :option:`kitty +kitten icat --scale-up` flag now takes effect when not using :option:`kitty +kitten icat --place` as well
- Add a mappable action :ac:`copy_last_command_output` to copy the output of the last
command to the clipboard (:pull:`9185`)
- ssh kitten: Fix a bug where automatic login was not working (:iss:`9187`)
- Graphics: Fix overwrite composition mode for animation frames not being honored
- Automatic color scheme switching: Fix title bar and scroll bar colors not being updated (:iss:`9167`)
- macOS: Fix cycle through OS windows only swapping between the two most recent
OS Windows. Also add a cycle through OS Windows backwards action.
(:iss:`9215`)
- :ac:`goto_session`: allow specifying a directory to select a session file
from the directory (:pull:`9219`)
- Have reloading config also reload the custom tab bar python modules (:disc:`9221`)
- kitten @ ls: Also output the neighbors for every window (:disc:`9225`)
- Have the :option:`kitty --start-as` flag be respected when used with
:option:`kitty --single-instance` (:iss:`9228`)
- When expanding environment variables in :opt:`listen_on` allow the :opt:`env`
directive to take effect
- macOS: Fix closing an OS Window when another OS Window is minimized causing
the minimized window to be un-minimized (:iss:`8913`)
- Do not rewrap the text in the alternate screen buffer. Avoids flicker during
live resize with no :opt:`resize_debounce_time` (:disc:`9142`)
- Add a default mapping :ac:`search_scrollback` to open the scrollback in a
pager in search mode. If any text is currently selected it is automatically
searched for.
- Wayland: Fix spurious key repeat events when some user defined callback takes
a long time to execute (:iss:`9224`)
- When moving windows to a new tab/OS Window fix overlay windows not being
grouped with their parent windows (:iss:`9266`)
- Linux: Fix a bug causing colors to occasionally all go black when using mesa
>= 25.3.0 with nouveau GPU driver (:iss:`9235`)
- Fix :opt:`tab_bar_min_tabs` not respecting :opt:`tab_bar_filter` (:iss:`9278`)
- macOS: Workaround for regression in Tahoe 26.2 that breaks :option:`kitty --detach`
(:iss:`9288`)
- macOS: Workaround for yet another Tahoe regression causing macOS to start an
AutoFill helper process and not shut it down on application exit (:iss:`9299`)
0.44.0 [2025-11-03]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Allow kitty to read a specified set of environment variables from your
login shell at startup using the :opt:`env` directive in kitty.conf
(:iss:`9042`)
- A new option :opt:`draw_window_borders_for_single_window` to force kitty to
always draw a window border even when only a single window is present
(:pull:`9112`)
- Fix a regression in 0.43.0 that caused a black flicker when closing a tab in
the presence of a background image (:iss:`9060`)
- Further improvements to rounded corner rendering, especially at low DPI
(:pull:`9091`)
- Splits layout: Fix a bug that could cause a corrupted layout in some
circumstances (:iss:`9059`)
- Fix a regression in the previous release that broke ``goto_session -1``
- Fix rendering broken on ancient GPU drivers that do not support rendering to 16 bit textures (:iss:`9068`)
- Fix tab bar sometimes showing incorrect tabs when it is filtered to show only
tabs from the current session (:iss:`9079`)
- macOS: Workaround for bug in macOS Tahoe that caused OS Windows that are
fullscreen to crash kitty when returning from sleep on some machines (:iss:`8983`)
- 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
- 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`)
- macOS: Handle dropping of file promises into kitty in addition to file paths (:pull:`9084`)
- macOS: Fix indeterminate progress bar displayed on dock icon increasing speed when indeterminate progress is set without being cleared first (:iss:`9114`)
- macOS: Performance and power usage improvements of about 5-10% (:pull:`9131`)
- macOS: Add an item to the global menu to Cycle through OS windows
- macOS: Quick access terminal: Fix a crash when changing font size (:iss:`9178`)
- Wayland: Fix ``center-sized`` panels not working on smithay based compositors (:pull:`9117`)
- Wayland: Fix scrolling using some mouse wheels that produce "VALUE120" based
scroll events too fast on some compositors (:pull:`9128`)
- Add support for Unicode 17
- Fix a regression in 0.43.0 that caused :opt:`tab_bar_margin_width` to be
doubled on the right edge of the tab bar (:iss:`9154`)
- Session files: Add a new ``focus_tab`` command to specify which tab should be
active when a session is loaded. Accepts either a plain number (0-based index)
or a match expression for flexible tab selection, allowing sessions to preserve
the active tab state (:doc:`sessions`)
- :ac:`save_as_session`: Add ``--base-dir`` option to specify a base directory
for saving session files with relative paths, useful when the current working
directory is not the desired location (:doc:`sessions`)
- Add ``state:focused_os_window`` match query to select all windows in the
currently focused OS window (:ref:`search_syntax`)
- Session saving now preserves visual tab order and active tab rather than tab
activation history as this is generally more important. In the future may
have it save tab history as well (:pull:`9163`)
- The :sc:`reset_terminal` shortcut to reset the terminal now also resets termios state
0.43.1 [2025-10-01]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ssh kitten: Allow specifying a password and/or TOTP authentication secret to
automate interactive logins in scenarios where public key authentication is
not supported (:pull:`9020`)
- macOS: Fix a bug where the color of a transparent titlebar was off when
running in the release build versus the build from source. Also fix using a
transparent titlebar causing the background opacity to be doubled.
- Fix a regression in the previous release that caused the incorrect tab to be
active when loading a session (:iss:`9025`)
- macOS: Workaround for bug in macOS Tahoe that caused closed OS Windows to
remain as invisible rectangles that intercept mouse events (:iss:`8952`)
- macOS: Fix a regression in the previous release that broke automatic
switching of dark/light mode when setting :opt:`macos_titlebar_color` to an
arbitrary color (:iss:`9034`)
- :ac:`goto_session`: Add ``--sort-by=alphabetical`` to have the interactive session
picker list the sessions in a fixed order rather than by most recent
(:disc:`9033`)
- Fix a regression in the previous release that caused the cursor trail to not
be hidden properly (:iss:`9039`)
- Session files: Fix a regression in the previous release that broke matching on
windows in the current tab (:iss:`9037`)
- Fix a regression in the previous release that broke clearing screen lines
when in margin mode (:iss:`9049`)
0.43.0 [2025-09-28]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- New support for creating and switching to :doc:`sessions` easily, allowing
users to define and use sessions/projects efficiently (:iss:`8911`)
- Add a configurable :opt:`scrollbar` for the kitty scrollback (:pull:`8945`)
- A new protocol for :doc:`multiple cursors <multiple-cursors-protocol>` in the terminal (:iss:`8927`)
- macOS: Allow the window title bar to be semi-transparent when
:opt:`background_opacity` is less than one and :opt:`macos_titlebar_color` is
set to ``background`` (:pull:`8906`)
- A new :opt:`cursor_trail_color` setting to independently control the color of
cursor trails (:pull:`8830`)
- macOS: Add the default :kbd:`Cmd+L` mapping from Terminal.app to erase the
last command and its output (:disc:`6040`)
- Fix :opt:`background_opacity` being non-linear especially with light color themes.
Note that this might require you to adjust the value of this setting to get
back your current look. (:iss:`8869`)
- **backward incompatibility**: :opt:`background_opacity` no longer applies to
:opt:`background_image` instead add an alpha channel to the image itself
- Add support for blinking text. Text marked as blinking now blinks in exact
rhythm with the cursor. The blinking animation and max duration are
controlled by :opt:`cursor_blink_interval` and
:opt:`cursor_stop_blinking_after`. (:pull:`8551`)
- Allow using a custom python function to draw tab titles in the tab bar, see
:opt:`tab_title_template`
- Wayland: Fix incorrect window size calculation when transitioning from
full screen to non-full screen with client side decorations (:iss:`8826`)
- macOS: Fix hiding quick access terminal window not restoring focus to
previously active application (:disc:`8840`)
- macOS: Fix showing the quick access terminal on a space other than the space
it was last active on, after full screening some application causes the quick
access terminal to appear on the old space (:iss:`8740`)
- macOS: When toggling open the quick access terminal move it to the currently
active monitor (the monitor with the mouse pointer on it) (:iss:`9003`)
- macOS: Fix closing an OS Window when another OS Window is minimized causing
the minimized window to be un-minimized (:iss:`8913`)
- Allow using backspace to move the cursor onto the previous line in cooked
mode. This is indicated by the `bw` property in kitty's terminfo
(:iss:`8841`)
- Watchers: A new event for global watchers corresponding to the tab bar being changed (:disc:`8842`)
- Fix a regression in 0.40.0 that broke handling of the VS16 variation selector
when it caused a character to flow to the next line (:iss:`8848`)
- Fix rendering of underlines when using larger text sizes with the space and
en-space characters (:iss:`8950`)
- Fix updating panel configuration on visibility toggle and via remote control
not working (:iss:`8984`)
- Improve rendering of rounded rectangles (:pull:`9000`)
- Wayland: Update bundled copy of libwayland to 1.24 from 1.23.1 because the
just released mesa 25.2.0 breaks with libwayland < 1.24 (:iss:`8884`)
- macOS: Pass the :kbd:`Cmd+C` shortcut to the application running in the
terminal when no text is selected (:pull:`8946`)
- macOS: Workaround for bug in macOS Tahoe that caused OS Windows that are
fullscreen on a monitor that is disconnected while macOS is asleep to crash kitty (:iss:`8983`)
0.42.2 [2025-07-16]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 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`)
- clipboard kitten: Can now optionally take a password to avoid repeated
permission prompts when accessing the clipboard. Based on a
:ref:`protocol extension <clipboard_repeated_permission>`. (:iss:`8789`)
- A new :option:`launch --hold-after-ssh` to not close a launched window
that connects directly to a remote host because of
:option:`launch --cwd`:code:`=current` when the connection ends (:pull:`8807`)
- Fix :opt:`remember_window_position` not working because of a stupid typo (:iss:`8646`)
- A new :option:`kitty --grab-keyboard` that can be used to grab the keyboard so that global shortcuts are sent to kitty instead
- 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`)
- hints kitten: Preserve line breaks when the hint is over a line break (:iss:`8674`)
- Fix a segfault when using the :ac:`copy_ansi_to_clipboard` action (:iss:`8682`)
- Fix a crash when using linear easing curves for animations (:iss:`8692`)
- Graphics protocol: Add a note clarifying image update behavior on re-transmission (:iss:`8701`)
- Wayland GNOME: Fix incorrect OS Window tracking because GNOME has started
activating windows on non-current workspaces (:iss:`8716`)
- Fix a regression in 0.40.0 that broke rendering of VS15 variation selectors in some circumstances (:iss:`8731`, :iss:`8794`)
- Fix a regression in 0.40.0 that broke serialization of tab characters as ANSI text (:iss:`8741`)
- Fix a regression in 0.40.0 that broke erasing of characters in a line in the presence of wide characters (:iss:`8758`)
- Fix a regression in 0.40.0 that broke hyperlinking of wide characters (:iss:`8796`)
- Fix a regression that broke using :kbd:`esc` to exit visual select window mode (:iss:`8767`)
- kitten run-shell: Fix SIGINT blocked when execing the shell (:iss:`8754`)
0.42.1 [2025-05-17]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Fix ambiguous width and private use characters not being rendered when used with variable width text-sizing protocol escape codes
- Quick access terminal: Restore focus to previously active window when hiding the quick access terminal window on macOS (:iss:`8627`)
- 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`)
- Quick access terminal: Allow toggling the window to full screen using the standard kitty :sc:`toggle_fullscreen` shortcut (:iss:`8626`)
- Quick access terminal: Allow configuring the monitor to display the panel on in Wayland/X11 (:iss:`8630`)
- 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`)
- Panel kitten: A new ``center-sized`` value for :option:`--edge <kitty +kitten panel --edge>` to allow easily creating sized and centered panels
- Wayland: The `kitty --name` flag now sets the XDG *window tag* on compositors
that support the `xdg-toplevel-tag <https://wayland.app/protocols/xdg-toplevel-tag-v1>`__ protocol.
0.42.0 [2025-05-11]
0.42.0 [future]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- A new kitten: :doc:`quick-access-terminal </kittens/quick-access-terminal>` to :ref:`quake`
- The :doc:`panel kitten </kittens/panel>` works on macOS and X11 as well as Wayland (:iss:`2590`)
- The :doc:`panel kitten </kittens/panel>` now works on macOS as well as Wayland (:iss:`2590`)
- **Behavior change**: Now kitty does full grapheme segmentation following the
Unicode 16 spec when splitting text into cells (:iss:`8533`)
@@ -871,7 +123,7 @@ Detailed list of changes
- launch: Allow creating desktop panels such as those created by the :doc:`panel kitten </kittens/panel>` (:iss:`8549`)
- Remote control: Allow modifying desktop panels and showing/hiding OS Windows
using the ``kitten @ resize-os-window`` command (:iss:`8550`)
using the `kitten @ resize-os-window` command (:iss:`8550`)
- Remote control launch: Allow waiting for a program launched in a new window
to exit and get the exit code via the `kitty +launch
@@ -908,7 +160,7 @@ Detailed list of changes
- :ac:`change_font_size` allow multiplying/dividing the current font size in addition to incrementing it (:iss:`8616`)
- Box drawing: Improve appearance of rounder corners, giving them a uniform line width (:iss:`8299`)
- Box drawing: Improve appearance of rounder corners giving them a uniform line width (:iss:`8299`)
0.41.1 [2025-04-03]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1285,7 +537,7 @@ Detailed list of changes
- Wayland labwc: Fix kitty timing out waiting for compositor to quit fucking around with scales on labwc (:iss:`7540`)
- Fix ``scrollback_indicator_opacity`` not actually controlling the opacity (:iss:`7557`)
- Fix :opt:`scrollback_indicator_opacity` not actually controlling the opacity (:iss:`7557`)
- URL detection: Fix IPv6 hostnames breaking URL detection (:iss:`7565`)
@@ -1369,7 +621,7 @@ Detailed list of changes
using the panel kitten for all compositors that support the `requisite Wayland
protocol <https://wayland.app/protocols/wlr-layer-shell-unstable-v1>`__ which is practically speaking all of them but GNOME (:pull:`2590`)
- Show a small scrollback indicator along the right window edge when viewing
- Show a small :opt:`scrollback indicator <scrollback_indicator_opacity>` along the right window edge when viewing
the scrollback to keep track of scroll position (:iss:`2502`)
- Wayland: Support fractional scales so that there is no wasted drawing at larger scale followed by resizing in the compositor
@@ -2132,7 +1384,7 @@ Detailed list of changes
window resize (:iss:`5162`)
- Remote control: Fix commands with large or asynchronous payloads like
:command:`kitty @ set-background-image`, :command:`kitty @ set-window-logo`
:command:`kitty @ set-backround-image`, :command:`kitty @ set-window-logo`
and :command:`kitty @ select-window` not working correctly
when using a socket (:iss:`5165`)
@@ -3234,7 +2486,7 @@ Detailed list of changes
do not display a box around active windows
- Add a new extensible escape code to allow terminal programs to trigger
desktop notifications. See :ref:`notifications_on_the_desktop` (:iss:`1474`)
desktop notifications. See :ref:`desktop_notifications` (:iss:`1474`)
- Implement special rendering for various characters from the set of "Symbols
for Legacy Computing" from the Unicode 13 standard

View File

@@ -15,7 +15,7 @@ of the escape code is::
<OSC>5522;metadata;payload<ST>
Here, *metadata* is a colon separated list of key-value pairs and payload is
base64 encoded data. :code:`OSC` is :code:`<ESC>]`.
base64 encoded data. :code:`OSC` is :code:`<ESC>[`.
:code:`ST` is the string terminator, :code:`<ESC>\\`.
Reading data from the system clipboard
@@ -29,11 +29,11 @@ For example, to read plain text and PNG data, the payload would be::
text/plain image/png
encoded as :rfc:`base64 <4648>`. To read from the primary selection instead of the
encoded as base64. To read from the primary selection instead of the
clipboard, add the key ``loc=primary`` to the metadata section.
To get the list of MIME types available on the clipboard the payload must be
just a period (``.``), encoded as :rfc:`base64 <4648>`.
just a period (``.``), encoded as base64.
The terminal emulator will reply with a sequence of escape codes of the form::
@@ -51,8 +51,7 @@ for an individual type, into chunks of size **no more** than 4096 bytes (4096
is the size of a chunk *before* base64 encoding). All
the chunks for a given type must be transmitted sequentially and only once they
are done the chunks for the next type, if any, should be sent. The end of data
is indicated by a ``status=DONE`` packet. base64 padding bytes are required at
the end of every base64 encoded chunk when padding is needed.
is indicated by a ``status=DONE`` packet.
If an error occurs, instead of the opening ``status=OK`` packet the terminal
must send a ``status=ERRORCODE`` packet. The error code must be one of:
@@ -139,72 +138,6 @@ the data, but create multiple references to it in the system clipboard. Alias
packets can be sent anytime after the initial write packet and before the end
of data packet.
.. _clipboard_repeated_permission:
Avoiding repeated permission prompts
--------------------------------------
.. versionadded:: 0.42.2
using a password to avoid repeated confirmations
If a program like an editor wants to make use of the system clipboard, by
default, the user is prompted on every read request. This can become quite
fatiguing. To avoid this situation, this protocol allows sending a password
and human friendly name with ``type=write`` and ``type=read`` requests. The
terminal can then ask the user to allow all future requests using that
password. If the user agrees, future requests on the same tty will be
automatically allowed by the terminal. The editor or other program using
this facility should ideally use a password randomly generated at startup,
such as a UUID4. However, terminals may implement permanent/stored passwords.
Users can then configure terminal programs they trust to use these password.
The password and the human name are encoded using the ``pw`` and ``name`` keys
in the metadata. The values are UTF-8 strings that are base64 encoded.
Specifying a password without a human friendly name is equivalent to not
specifying a password and the terminal must treat the request as though
it had no password.
Allowing terminal applications to respond to paste events
--------------------------------------------------------------
.. versionadded:: 0.44.1
paste events via the 5522 mode
If a TUI application wants to handle paste events (like the user pressing the
paste key shortcut used by the terminal or selecting paste from a terminal UI menu)
it can enable the *paste events* private mode (5522), as described in this `ancillary
specification <https://rockorager.dev/misc/bracketed-paste-mime/>`__. When that
mode is set, the terminal will send the application a list of MIME types on the
clipboard every time the user triggers a paste action. The application is then
free to request whatever MIME data it wants from the list of types.
The mode can be enabled using the standard DECSET or DECRST control sequences.
``CSI ? 5522 h`` to enable the mode. ``CSI ? 5522 l`` to disable the mode.
The terminal *should* send a one time password with the list of mime
types, as the ``pw`` key (base64 encoded). The application can then use this
password to request data from the clipboard without needing a permission
prompt. The human name *should* be set to ``Paste event`` (base64 encoded) when
the application uses this one time password.
Detecting support for this protocol
-----------------------------------------
Applications can detect if a terminal supports this protocol with a standard
DECRQM query:
.. code::
CSI ? 5522 $ p
To which the terminal will respond with a DECRPM response:
.. code::
CSI ? 5522 ; Ps $ y
A Ps value of 0 or 4 means the mode is not supported.
Support for terminal multiplexers
------------------------------------
@@ -219,15 +152,11 @@ other characters must be stripped out from the id by the terminal emulator
before retransmitting it.
Note that when using a terminal multiplexer it is possible for two different
programs to overwrite each other's clipboard requests. This is fundamentally
programs to overwrite each others clipboard requests. This is fundamentally
unavoidable since the system clipboard is a single global shared resource.
However, there is an additional complication where responses from this protocol
However, there is an additional complication where responses form this protocol
could get lost if, for instance, multiple write requests are received
simultaneously. It is up to well designed multiplexers to ensure that only a
single request is in flight at a time. The multiplexer can abort requests by
sending back the ``EBUSY`` error code indicating some other window is trying
to access the clipboard.
When the terminal sends an unsolicited paste event because the user triggered
a paste and the 5522 mode is enabled, there will be no associated id. In this
case, the multiplexer must forward the event to the currently active window.

View File

@@ -52,8 +52,8 @@ than numbers. The syntax of the escape code is::
<OSC> 21 ; key=value ; key=value ; ... <ST>
The spaces in the above definition are for reading clarity and should be ignored.
Here, ``<OSC>`` is the two bytes ``0x1b (ESC)`` and ``0x5d (])``. ``<ST>`` is
either ``0x07 (BEL)`` or the two bytes ``0x1b (ESC)`` and ``0x5c (\\)``.
Here, ``<OSC>`` is the two bytes ``0x1b (ESC)`` and ``0x5d (])``. ``ST`` is
either ``0x7 (BEL)`` or the two bytes ``0x1b (ESC)`` and ``0x5c (\\)``.
``key`` is a number from 0-255 to query or set the color values from the
terminals ANSI color table, or one of the strings in the table below for
@@ -69,7 +69,7 @@ selection_foreground The foreground color of selections
cursor The color of the text cursor Foreground color
cursor_text The color of text under the cursor Background color
visual_bell The color of a visual bell Automatic color selection based on current screen colors
transparent_background_color1..7 A background color that is rendered Unset
transparent_background_color1..8 A background color that is rendered Unset
with the specified opacity in cells that have
the specified background color. An opacity
value less than zero means, use the
@@ -103,7 +103,7 @@ This indicates that the foreground color is red and the cursor color is
undefined (typically the cursor takes the color of the text under it and the
text takes the color of the background).
If the terminal does not know a field that a client sends to it for a query it
If the terminal does not know a field that a client send to it for a query it
must respond back with the ``field=?``, that is, it must send back a question
mark as the value.

View File

@@ -18,9 +18,8 @@ from typing import Any, Callable, Dict, Iterable, Iterator, List, Tuple
from docutils import nodes
from docutils.parsers.rst.roles import set_classes
from pygments.lexer import RegexLexer
from pygments.lexer import bygroups as untyped_bygroups
from pygments.token import Comment, Error, Keyword, Literal, Name, Number, String, Whitespace
from pygments.lexer import RegexLexer, bygroups # type: ignore
from pygments.token import Comment, Error, Keyword, Literal, Name, Number, String, Whitespace # type: ignore
from sphinx import addnodes, version_info
from sphinx.util.logging import getLogger
@@ -30,7 +29,7 @@ if kitty_src not in sys.path:
from kitty.conf.types import Definition, expand_opt_references # noqa
from kitty.constants import str_version, website_url # noqa
from kitty.fast_data_types import DND_CODE, Shlex, TEXT_SIZE_CODE # noqa
from kitty.fast_data_types import Shlex, TEXT_SIZE_CODE # noqa
# config {{{
# -- Project information -----------------------------------------------------
@@ -121,7 +120,6 @@ string_replacements = {
'_kitty_install_cmd': 'curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin',
'_build_go_version': go_version('../go.mod'),
'_text_size_code': str(TEXT_SIZE_CODE),
'_dnd_code': str(DND_CODE),
}
@@ -239,13 +237,6 @@ def write_cli_docs(all_kitten_names: Iterable[str]) -> None:
usage='file-or-dir-to-copy ...', message=copy_message
))
del sys.modules['kittens.ssh.main']
from kitty.session import save_as_session_message, save_as_session_options
with open('generated/save-as-session.rst', 'w') as f:
f.write(option_spec_as_rst(
appname='save_as_session', ospec=save_as_session_options, heading_char='^',
usage='[path-to-save-session-file-at]',
message=save_as_session_message,
))
from kitty.launch import options_spec as launch_options_spec
with open('generated/launch.rst', 'w') as f:
@@ -302,7 +293,7 @@ if you specify a program-to-run you can use the special placeholder
def write_color_names_table() -> None: # {{{
from kitty.fast_data_types import all_color_names
from kitty.rgb import color_names
def s(c: Any) -> str:
return f'{c.red:02x}/{c.green:02x}/{c.blue:02x}'
with open('generated/color-names.rst', 'w') as f:
@@ -310,7 +301,7 @@ def write_color_names_table() -> None: # {{{
p('=' * 50, '=' * 20)
p('Name'.ljust(50), 'RGB value')
p('=' * 50, '=' * 20)
for name, col in all_color_names():
for name, col in color_names.items():
p(name.ljust(50), s(col))
p('=' * 50, '=' * 20)
# }}}
@@ -369,45 +360,41 @@ def replace_string(app: Any, docname: str, source: List[str]) -> None: # {{{
# config file docs {{{
def bygroups(*args: Any) -> Any:
return untyped_bygroups(*args) # type: ignore[no-untyped-call]
class ConfLexer(RegexLexer):
class ConfLexer(RegexLexer): # type: ignore
name = 'Conf'
aliases = ['conf']
filenames = ['*.conf']
def map_flags(self: RegexLexer, val: str, start_pos: int) -> Iterator[Tuple[int, Any, str]]:
expecting_arg = ''
s = Shlex(val)
from kitty.options.utils import allowed_key_map_options
last_pos = 0
while (tok := s.next_word())[0] > -1:
x = tok[1]
if tok[0] > last_pos:
yield start_pos + last_pos, Whitespace, ' ' * (tok[0] - last_pos)
last_pos = tok[0] + len(x)
tok_start = start_pos + tok[0]
if expecting_arg:
yield tok_start, String, x
expecting_arg = ''
elif x.startswith('--'):
expecting_arg = x[2:]
k, sep, v = expecting_arg.partition('=')
k = k.replace('-', '_')
expecting_arg = k
if expecting_arg not in allowed_key_map_options:
yield tok_start, Error, x
elif sep == '=':
expecting_arg = ''
s = Shlex(val)
from kitty.options.utils import allowed_key_map_options
last_pos = 0
while (tok := s.next_word())[0] > -1:
x = tok[1]
if tok[0] > last_pos:
yield start_pos + last_pos, Whitespace, ' ' * (tok[0] - last_pos)
last_pos = tok[0] + len(x)
tok_start = start_pos + tok[0]
if expecting_arg:
yield tok_start, String, x
expecting_arg = ''
yield tok_start, Name, x
elif x.startswith('--'):
expecting_arg = x[2:]
k, sep, v = expecting_arg.partition('=')
k = k.replace('-', '_')
expecting_arg = k
if expecting_arg not in allowed_key_map_options:
yield tok_start, Error, x
elif sep == '=':
expecting_arg = ''
yield tok_start, Name, x
else:
yield tok_start, Name, x
else:
yield tok_start, Name, x
else:
break
break
def mapargs(self: 'ConfLexer', match: 're.Match[str]') -> Iterator[Tuple[int, Any, str]]:
def mapargs(self: RegexLexer, match: 're.Match[str]') -> Iterator[Tuple[int, Any, str]]:
start_pos = match.start()
val = match.group()
parts = val.split(maxsplit=1)
@@ -471,7 +458,7 @@ class ConfLexer(RegexLexer):
}
class SessionLexer(RegexLexer):
class SessionLexer(RegexLexer): # type: ignore
name = 'Session'
aliases = ['session']
filenames = ['*.session']
@@ -656,10 +643,33 @@ def monkeypatch_man_writer() -> None:
'''
Monkeypatch the docutils man translator to be nicer
'''
from docutils.nodes import figure
from docutils.writers.manpage import Translator
from docutils.nodes import Element
from docutils.writers.manpage import Table, Translator
from sphinx.writers.manpage import ManualPageTranslator
# Generate nicer tables https://sourceforge.net/p/docutils/bugs/475/
class PatchedTable(Table): # type: ignore
_options: list[str]
def __init__(self) -> None:
super().__init__()
self.needs_border_removal = self._options == ['center']
if self.needs_border_removal:
self._options = ['box', 'center']
def as_list(self) -> list[str]:
ans: list[str] = super().as_list()
if self.needs_border_removal:
# remove side and top borders as we use box in self._options
ans[2] = ans[2][1:]
a, b = ans[2].rpartition('|')[::2]
ans[2] = a + b
if ans[3] == '_\n':
del ans[3] # top border
del ans[-2] # bottom border
return ans
def visit_table(self: ManualPageTranslator, node: object) -> None:
setattr(self, '_active_table', PatchedTable())
setattr(ManualPageTranslator, 'visit_table', visit_table)
# Improve header generation
def header(self: ManualPageTranslator) -> str:
@@ -676,13 +686,13 @@ def monkeypatch_man_writer() -> None:
setattr(ManualPageTranslator, 'header', header)
def visit_image(self: ManualPageTranslator, node: figure) -> None:
def visit_image(self: ManualPageTranslator, node: Element) -> None:
pass
def depart_image(self: ManualPageTranslator, node: figure) -> None:
def depart_image(self: ManualPageTranslator, node: Element) -> None:
pass
def depart_figure(self: ManualPageTranslator, node: figure) -> None:
def depart_figure(self: ManualPageTranslator, node: Element) -> None:
self.body.append(' (images not supported)\n')
Translator.depart_figure(self, node)
@@ -690,8 +700,8 @@ def monkeypatch_man_writer() -> None:
setattr(ManualPageTranslator, 'depart_image', depart_image)
setattr(ManualPageTranslator, 'depart_figure', depart_figure)
orig_astext = getattr(ManualPageTranslator, 'astext')
def astext(self: ManualPageTranslator) -> Any:
orig_astext = Translator.astext
def astext(self: Translator) -> Any:
b = []
for line in self.body:
if line.startswith('.SH'):
@@ -700,9 +710,9 @@ def monkeypatch_man_writer() -> None:
parts[0] = parts[0].capitalize()
line = x + ' ' + '\n'.join(parts)
b.append(line)
setattr(self, 'body', b)
self.body = b
return orig_astext(self)
setattr(ManualPageTranslator, 'astext', astext)
setattr(Translator, 'astext', astext)
def setup_man_pages() -> None:

View File

@@ -16,8 +16,7 @@ frames-per-second. See below for an overview of all customization possibilities.
You can open the config file within |kitty| by pressing :sc:`edit_config_file`
(:kbd:`⌘+,` on macOS). A :file:`kitty.conf` with commented default
configurations and descriptions will be created if the file does not exist.
The configuration is automatically reloaded when modified, controlled by
:opt:`auto_reload_config`. You can manually reload the config by pressing
You can reload the config file within |kitty| by pressing
:sc:`reload_config_file` (:kbd:`⌃+⌘+,` on macOS) or sending |kitty| the
``SIGUSR1`` signal with ``kill -SIGUSR1 $KITTY_PID``. You can also display the
current configuration by pressing :sc:`debug_config` (:kbd:`⌥+⌘+,` on macOS).
@@ -63,6 +62,10 @@ by running a program using :code:`geninclude`. For example::
geninclude dynamic.py
.. note:: Syntax highlighting for :file:`kitty.conf` in vim is available via
`vim-kitty <https://github.com/fladson/vim-kitty>`__.
.. include:: /generated/conf-kitty.rst
@@ -98,4 +101,3 @@ See the :doc:`list of all the things you can make |kitty| can do </actions>`.
:hidden:
actions
wide-gamut-colors

View File

@@ -1,4 +1,4 @@
.. _notifications_on_the_desktop:
.. _desktop_notifications:
Desktop notifications
@@ -25,7 +25,7 @@ the set :code:`a-zA-Z0-9-_/\+.,(){}[]*&^%$#@!`~`. The payload must be
interpreted based on the metadata section. The two semi-colons *must* always be
present even when no metadata is present.
Before going into details, let's see how one can display a simple, single line
Before going into details, lets see how one can display a simple, single line
notification from a shell script::
printf '\x1b]99;;Hello world\x1b\\'
@@ -395,7 +395,7 @@ Key Value
``c`` ``c=1`` if the terminal supports close events, otherwise the ``c``
must be omitted.
``o`` Comma separated list of occasions from the ``o`` key that the
``o`` Comma separated list of occassions from the ``o`` key that the
terminal implements. If no occasions are supported, the value
``o=always`` must be sent in the query response.
@@ -405,7 +405,7 @@ Key Value
``s`` Comma separated list of sound names from the table of standard sound names above.
Terminals will report the list of standard sound names they support.
Terminals *should* support at least ``system`` and ``silent``.
Terminals *should* support atleast ``system`` and ``silent``.
``u`` Comma separated list of urgency values that the terminal implements.
If urgency is not supported, the ``u`` key must be absent from the
@@ -450,10 +450,10 @@ Key Value Default Description
encoded UTF-8
application name
``g`` :ref:`identifier` ``unset`` Identifier for icon data. Make these globally unique,
``g`` :ref:`identifier` ``unset`` Identifier for icon data. Make these globally unqiue,
like an UUID.
``i`` :ref:`identifier` ``unset`` Identifier for the notification. Make these globally unique,
``i`` :ref:`identifier` ``unset`` Identifier for the notification. Make these globally unqiue,
like an UUID, so that terminal multiplexers can
direct responses to the correct window. Note that for backwards
compatibility reasons i=0 is special and should not be used.

View File

@@ -1,561 +0,0 @@
The Drag and Drop protocol
==============================================
.. versionadded:: 0.47.0
This protocol enables drag and drop functionality for terminal programs
that is as good as the drag and drop functionality available for GUI
programs.
There is one central escape code used for this protocol, which is of the form::
OSC _dnd_code ; metadata ; payload ST
Here, ``OSC`` is the bytes ``ESC ] (0x1b 0x5b)`` and ST is ``ESC \\ (0x1b 0x5c)``.
The ``metadata`` is a colon separated list of ``key=value`` pairs.
The final part of the escape code is the payload data, whose meaning depends on the metadata.
The payload must be no more than 4096 bytes. When the payload is larger than 4096
bytes, it is chunked up using the ``m`` key. An escape code that has a too long
payload is transmitted in chunks. All but the last chunk must have ``m=1`` in
their metadata. Each chunk must have a payload of no more than 4096 bytes.
Only the first chunk is guaranteed to have metadata other than the ``m`` key.
Subsequent chunks may optionally omit all
metadata except the ``m`` and ``i`` keys. While a chunked transfer is in
progress it is a protocol error to for the sending side to
send any protocol related escape codes other than chunked ones or query (``t=q|Q``) ones.
In particular, this means that the receiving side should use the metadata from
the first chunk in a chain of chunks only.
All integer values used in this escape code must be 32-bit signed or unsigned
integers encoded in decimal representation.
When transferring binary data the payload is :rfc:`base64 <4648>` encoded. The
4096 bytes limit applies to *encoded bytes*, that is, it is applied after
encoding. base64 padding bytes are optional and may or may not be present at
the end of the last chunk.
Accepting drops
-----------------
In order to inform the terminal emulator that the client accepts drops, it
must send the following escape code::
OSC _dnd_code ; t=a ; payload ST
The payload here is a space separated list of MIME types the program accepts.
The list of MIME types is optional, it is needed if the program wants to accept
exotic or private use MIME types on platforms such as macOS, where the system
does not deliver drop events unless the MIME type is registered.
When the client is done accepting drops, or at exit, it should send the escape
code::
OSC _dnd_code ; t=A ST
to inform the terminal that it no longer wants drops.
Whenever the user drags something over the window, the terminal will send an
escape code of the form::
OSC _dnd_code ; t=m:x=x:y=y:X=X:Y=Y:o=O ; optional MIME list ST
Here, ``x, y`` identify the cell over which the drag is currently present.
The ``(0, 0)`` cell is at top left of the screen. ``X and Y`` are the pixel
offsets from the top-left. The ``O`` value is the set of allowed operations,
which can be ``1`` for copy, ``2`` for move and ``3`` for either, at the
client's discretion. The optional list of MIMES is a space separated
list of MIME types that are available for dropping. To avoid overhead, the
terminal should only send this list for the first move event and subsequently
only if the list changes.
When the drag leaves the window, the terminal will send the same event but
with ``x, y = -1, -1`` to indicate that the drag has left the window. For such
events the list of MIME types must be empty. Note that the terminal must never
send negative cell co-ordinates for any other reason. No more movement escape
codes ``t=m`` will be sent until this drop or another re-enters the window.
The client program must inform the terminal whether it will accept
the potential drop and which MIME types of the set of offered MIME types it
accepts. Until the client does so the terminal will indicate to the OS that
the drop is not accepted. To do so, the client sends an escape code of the
form::
OSC _dnd_code ; t=m:o=O ; MIME list ST
Here the ``o`` key is the operation the client intends to perform if a drop
occurs which can be either ``1`` for copy or ``2`` for move or ``0`` for not
accepted. The MIME list is the ordered list of MIME types from the offered list
that the client wants. If no MIME type list is present, it is equivalent to no
change in the offered list of MIME types. The list should be ordered in order
of decreasing preference. Some platforms may show the user some
indication of the first MIME type in the list.
When the user triggers a drop on the window, the terminal will send an escape
code of the form::
OSC _dnd_code ; t=M: ... ; MIME list ST
This is the same as the movement escape codes above, except that ``t=M``
(upper case M instead of lower case m), indicating this is a drop.
Once this escape code is received, no more movement escape codes ``t=m``
will be sent until a new drop enters the window. The MIME list here is
mandatory, terminals must send the full list of MIME types available in
the drop. The client program can now request data for the MIME types
it is interested in.
Requesting data is done by sending an escape code of the form::
OSC _dnd_code ; t=r:x=idx ST
Here ``idx`` is a 1-based index into the list of MIME types sent in the ``t=M``
drop event. This will request data for the specified MIME type.
The terminal must respond with a series of escape codes of the form::
OSC _dnd_code ; t=r:x=idx; base64 encoded data possibly chunked ST
End of data is indicated by an empty payload and ``m=0``. If some error occurs while
getting the data, the terminal must send an escape code of the form::
OSC _dnd_code ; t=R:x=idx ; POSIX error name:optional description ST
Here ``POSIX error name`` is a POSIX symbolic error name such as ``ENOENT`` or
``EIO`` or the value ``EUNKNOWN`` for an unknown error. Unless otherwise noted,
any error response means the drop is terminated. The description is optional
and must consist only of :ref:`safe_utf8`.
Once the client program finishes reading all the dropped data it needs, it must
send an escape code of the form::
OSC _dnd_code ; t=r:o=operation ST
That is, it must send a request for data with no MIME type specified. The
terminal emulator must then inform the OS that the drop is completed. Any
queued data requests must be discarded by the terminal. The ``operation``
is required and must specify the final action the client took with the data.
If unset (aka ``0``) the terminal must assume the drop was canceled.
Dropping from remote machines
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In order to support dropping of files from remote machines, the client
must inform the terminal of its :ref:`machine id <machine_id>` using the escape code::
OSC _dnd_code ; t=a:x=1 ; machine id ST
Then, the client must first request the :rfc:`text/uri-list <2483>` MIME
type to get a list of dropped URIs. When responding to this request,
the terminal will send the usual ``t=r`` responses, but, in addition,
if the client has sent its machine id and the terminal determines that
the client is on a different machine based on the id, it will add the ``X=1``
key to its response. The client should use this key to determine if it wants to
request data for entries in the URI list. For every URI in the list, the client can
send the terminal emulator a data request of the form::
OSC _dnd_code ; t=r:x=idx:y=subidx ST
Here ``idx`` is the one based index of the ``text/uri-list`` MIME type. And
``subidx`` is the one based index into the array of MIME types in
the ``text/uri-list`` entry. The terminal will then read the file and
transmit the data as for a normal MIME data request, except it will have
``y=subidx`` as well in its response, for example::
OSC _dnd_code ; t=r:x=idx:y=subidx ; base64 encoded data possibly chunked ST
Similarly, error responses are as above, except for the addition of
``y=subidx``, for example::
OSC _dnd_code ; t=R:x=idx:y=subidx ; POSIX error name:optional desc ST
Terminals must reply with ``ENOENT`` if the index is out of bounds.
If the client does not first request the ``text/uri-list`` MIME type or that
MIME type is not present in the drop, the terminal must reply with
``EINVAL``. Terminals must support at least ``file://`` URIs.
If the client requests an entry that is not a supported URI type the
terminal must reply with ``EUNKNOWN``.
Terminals must ONLY send data for regular files, symbolic links and directories.
See below for more details about sending symlinks.
If the terminal does not have permission to read the file it must reply with
``EPERM``. Terminals must respond with ``EINVAL`` if the file is not a regular
file or symlink or directory and ``ENOENT`` if the file does not exist. If an
I/O error occurs the terminal must send ``EIO``.
For security reasons, terminals must reply with ``EPERM`` if the drag
originated in the same window as the drop, this prevents malicious programs
from reading files on the computer by starting their own drag. This is a
defense in depth feature since drags can only be started by the terminal, but
it helps in case of accidental drag starts and drops into the same window.
Clients may send multiple requests without waiting for any request to complete.
Terminals may queue requests and respond in any order they choose, including
interleaving responses to different requests. However, for simplicity, this
specification recommends terminals queue requests and respond in first-in,
first-out order. Every response can be matched to a corresponding request
using the ``x``, ``y`` and ``Y`` keys. To prevent Denial of service attacks,
if too many requests are received, terminals must deny the request
with ``EMFILE`` and end the drop.
Reading remote directories
+++++++++++++++++++++++++++
If the file pointed to by a ``file://`` URL is actually a directory the terminal must instead respond with::
OSC _dnd_code ; t=r:x=idx:y=subidx:X=handle ; base64 encoded list of dir entries ST
The presence of ``X=handle`` indicates this is a directory response not a regular
file or symlink. For regular files, ``X=0``. For symlinks, ``X=1``.
For directories ``X`` is an arbitrary integer (``handle``) other than ``0`` or ``1``.
Here, the payload is a null byte separated list of entries in the directory that are
either regular files, directories or symlinks. The payload must be base64
encoded and might be chunked if the directory has a lot of entries.
The client can now read the files in this directory using requests of the form::
OSC _dnd_code ; t=r:Y=handle:x=num ST
Here ``num`` is the 1-based index into the list of directory entries previously transmitted
to the client. The terminal will respond with an escape code of the forms ::
OSC _dnd_code ; t=r:Y=handle:x=num ; base64 encoded data of regular file ST
OSC _dnd_code ; t=r:Y=handle:x=num:X=1 ; base64 encoded symlink target ST
OSC _dnd_code ; t=r:Y=handle:x=num:X=child-handle ; base64 encoded list of entries in sub-dir ST
In case of any errors, the terminal will respond with::
OSC _dnd_code ; t=R:Y=handle:x=num ; POSIX error name:optional desc ST
In the above, the ``Y=handle`` and ``x=num`` keys allow the client to know
which directory entry the response concerns. The ``handle`` points to the
parent directory and ``num`` to the entry within the parent dir.
Once the client is done reading a directory it should transmit ``t=r:Y=handle`` to the terminal. The
terminal can then free any resources associated with that directory. The
directory handle is now invalid and terminals must return ``EINVAL`` if the
client sends a request using an invalid directory handle. It is recommended
that clients traverse directories breadth first to minimise resource usage in
the terminal. Terminals may deny directory traversal requests if too many
resources are used, in order to prevent Denial of service attacks. In such
cases the terminal must respond with ``ENOMEM``.
Starting drags
-----------------
Terminal programs can inform the terminal emulator that they
are willing to act as a source of drag data by sending the
sending the escape code::
OSC _dnd_code ; t=o:x=1 ; optional machine id ST
On exit, or if the program no longer is willing to start drag gestures, it must
send ``t=o:x=2`` to the terminal to indicate it no longer wants to offer drag data.
The ``machine id`` is optional and is used to enable dragging from remote
machines. See :ref:`below <machine_id>` for its semantics.
When the user performs the platform specific gesture to start a drag operation,
the terminal will send the escape code ``t=o`` back to the terminal program
informing it that it can potentially start a drag. The gesture is typically holding the
left mouse button down and dragging a short distance, but this protocol does
not mandate any particular gesture to start drag operations. The terminal, when
sending the event will also set the ``x, y, X, Y`` keys to indicate the cell
and pixel locations in the window of the start drag event.
If the terminal program determines that it wants to start a drag at that
location, it must send the terminal the ``t=o:o=flags`` escape code again, but
with a payload consisting of the space separated MIME types it offers. The
``flags`` indicate what types of operations the client supports, ``1`` for
copy, ``2`` for move and ``3`` for either. The transmission should be chunked
if the list of MIME types is too long. Note that at this time the drag
operation has not actually started, this gives the terminal program the
opportunity to pre-send some data or set one or more images to act as
thumbnails for the drag operation. If the list of MIME types is too long the
terminal may cancel the operation by responding with ``t=E ; EFBIG`` or ``t=E ;
ENOMEM``.
If at the time the terminal receives this request the drag gesture has already
been terminated or the terminal otherwise determines that it is not appropriate
to start the drag, it must reply with ``t=E ; EPERM`` to indicate the drag
offer was not accepted.
For some well known types like ``text/plain`` or ``text/uri-list`` the
terminal program should pre-send the data for them unless it is very large.
This is because some platforms, such as macOS, need pre sent data to be able
to interoperate with native programs. The terminal emulator should reply with
``t=E ; EFBIG`` if too much data is sent and cancel the drag. Terminals must
accept at least 64MB of pre sent data.
Pre sent data is sent with escape codes of the form::
OSC _dnd_code ; t=p:x=idx ; base64 encoded data ST
Here ``idx`` is the zero based index into the list of previously sent MIME
types indicating this data is for that MIME type. Transmission should be chunked
using the ``m`` key. End of data is indicated by sending the escape code with no
payload and ``m=0``. Terminal programs should pre-read this data and only send
the ``t=o`` key indicating the offer if the data is available.
To associate one or more images with the drag operation, the terminal program
must transmit the data for the image with the ``idx`` value above being a
negative number starting with ``-1`` for the first image and so on. Clients
**must** transmit all images consecutively in order, starting with the first,
then the second and so on. When transmitting images, the image data format is
specified using the ``y`` key. A value of ``y=24`` mean 24bit RGB data and
``y=32`` means 32bit RGBA data. Colors in the RGB/A data must be in the sRGB
color space. Using ``y=100`` means the data is a PNG image. Using ``y=0`` means
the payload is UTF-8 base64 encoded text. The terminal will render the text to
display the image. Additionally, the ``X`` and ``Y`` keys must be used to
specify the width and height of the image data in pixels. If the size of the
transmitted data does not match the image dimensions the terminal must replay
with ``t=E ; EINVAL``. When using ``y=0`` the ``X`` and ``Y`` keys specify the
text size as ``base_font_size * X/Y``. Where ``base_font_size`` is the normal
font size the terminal uses for text. Note that terminals **may** ignore
newlines and render all text on a single line so client programs should not
send too much text. The ``o`` key is used to specify if the background the
text is drawn on is transparent or not. ``o=0`` means transparent and ``o=1024``
means fully opaque. In other words background opacity is ``o/1024``.
This is particularly useful when drawing a Unicode symbol as the icon for the drag.
Terminals are free to impose a limit on the amount of image data, to avoid
Denial-of-service attacks. If the image data is too much or the image is too
large they must reply with ``t=E ; EFBIG`` and abort the drag. By default, the
drag will be started using the first image, if any. During the drag, the
terminal program can change the image by sending::
OSC _dnd_code ; t=P:x=idx ST
Where ``idx`` is now a zero based index with zero being the first image and so on.
Sending an ``idx`` out of bounds means the drag image should be removed.
Once the terminal program has sent all data and images for the drag
operation, it indicates the drag should be started by sending ``t=P:x=-1``. At
this time if the user has already cancelled the drag or the terminal determines
the drag operation is not allowed, it must respond with ``t=E ; EPERM``. If any
other error occurs starting the drag operation, it must respond with the appropriate
POSIX error name and optional error description. If it determines that the image data after conversion to
display format is too large, it must respond with ``t=E ; EFBIG``. If the drag
operation is successfully started, it must respond with ``t=E ; OK``.
As the drag progresses, status changes are reported using the ``t=e`` escape
code. The variants are listed in the table below:
.. list-table:: Drag offer events
* - Code
- Description
* - ``t=e : x=1 : y=idx``
- The drag has been accepted by a client. ``idx`` is a zero based index into the list of MIME types
pointing to the MIME type the client is likely to want
* - ``t=e : x=2 : o=O``
- The action the client is likely to perform has changed to the value indicated by the ``o`` key
* - ``t=e : x=3``
- The drag offer has been dropped onto a client, there are likely to be requests for data in the near future
* - ``t=e : x=4 : y=0 or 1``
- The drag is finished. If ``y=1`` then the drag was canceled by the user.
* - ``t=e : x=5 : y=idx``
- Request data for the MIME type at the zero based index ``idx`` in the list of MIME types
The client program should respond to data requests with escape codes of the
form::
OSC _dnd_code ; t=e:y=idx:m=0 or 1 ; base64 encoded data ST
This, is the data for the MIME type identified by ``idx`` which is a zero based
index into the list of MIME types. The data should be chunked using the
``m`` key. End of data is denoted by ``m=0`` and an empty payload. If an error
occurs the client should send::
OSC _dnd_code ; t=E:y=idx ; POSIX error name:optional description ST
Where ``POSIX error name`` is a POSIX symbolic error name such as ``ENOENT``
if the MIME type is not found or ``EIO`` if an IO error occurred and so on. The
description is optional and must contain only :ref:`safe_utf8`.
If the client wants to cancel the full drag at any time, it should send::
OSC _dnd_code ; t=E:y=-1 ST
If ``t=e`` or ``t=E`` escape codes are sent to the terminal before the drag is
started and the terminal has responded with ``t=E ; OK``, the terminal must respond
with ``t=E ; EINVAL`` and abort the drag.
Dragging to remote machines
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To support dragging to remote machines, first of all clients **must** pre-send
the data for the ``text/uri-list`` MIME type. All ``file://`` URLs that point
to directories **must** end with a ``/``. Then, terminals can
examine the :ref:`machine_id` sent with the enable drag offers
``t=o`` escape code to decide whether to request data for ``file://`` entries
from the URI list. To request data for a particular entry, terminals send an
escape code of the form::
OSC _dnd_code ; t=k:x=idx ST
Here ``idx`` is the one based index into the list of entries in the
``text/uri-list`` MIME type. Then the client can respond with the data
for that entry with an escape code of the form::
OSC _dnd_code ; t=k:x=idx:m=0 or 1 ; base64 encoded file data ST
OSC _dnd_code ; t=k:x=idx:X=1:m=0 or 1 ; base64 encoded symlink target ST
OSC _dnd_code ; t=k:x=idx:X=handle:m=0 or 1 ; base64 encoded null separated list of directory entries ST
These represent possibly chunked data for files, symlinks and directories, as
denoted by the ``X`` key. As above, end of data for an individual entry is
indicated by an escape code with ``m=0`` and no payload. ``idx`` is the one
based index into the list of entries in the ``text/uri-list`` MIME type.
Only regular files, symlinks and directories should be sent. Terminals may send
multiple requests before the data for the first request is received. Client
programs should queue the requests and handle them in FIFO order.
Every directory must be transmitted with ``X=handle``. The payload
is a null separated list of regular files, directories and symlinks in the
directory. ``handle`` is an integer other than ``0`` or ``1``
that serves as an identifier for the directory. Directories should be traversed
in breadth first order. The children of a directory are sent by
adding ``Y=parent-handle:y=num`` to the escape codes above. Here
``parent-handle`` is the handle of the directory being traversed and ``num``
is the one based index into the list of entries in the directory. Thus, the
set of keys ``x, y, Y`` uniquely determine an entry. Clients **must** send all
children of directories, recursively, terminals **must not** make requests
for children of directories, only for the entries from the ``text/uri-list``.
If any error occurs in the client while reading the data, it can inform
the terminal using::
OSC _dnd_code ; t=E ; POSIX error name:optional description ST
The terminal must then abort the drag.
Terminals are free to impose resource limits on how much data they accept,
if a limit is breached or some errors occurs, they can abort the drag and
inform the client of it with::
OSC _dnd_code ; t=E ; POSIX error name:optional description ST
The error code for too many resources is ``EMFILE``,
for IO errors is ``EIO`` and so on.
Detecting support for this protocol
-------------------------------------
Clients can query the terminal emulator for support of this protocol
using::
OSC _dnd_code ; t=q:i=optional ST
The ``i`` key is optional, if present it will be echoed back in the responses
from the terminal. A terminal supporting this protocol **must** respond with::
OSC _dnd_code ; t=q:i=echoed ; payload ST
Here, ``payload`` is a colon separated list of ``key=value`` pairs. These
specify support for optional/future parts of this protocol. Currently the
payload is empty, but that might change as the protocol evolves.
The client should send these escape codes followed by a request for the `primary device
attributes <https://vt100.net/docs/vt510-rm/DA1.html>`_. If a response for the
device attributes is received before a response for the queries, then the
terminal does not support this protocol.
Multiplexers
-----------------
To support multiplexers, the ``i`` key exists. When the terminal receives a
``t=a`` or ``t=o`` escape code that has the ``i`` key set, all escape codes it
sends to the terminal program must include the ``i`` key with the same value.
This allows terminal multiplexers to direct the response codes to the correct
client.
Metadata reference
---------------------------
The table below shows all the metadata keys as well as what values they can
take, and the default value they take when missing. All integers are 32-bit.
======= ==================== ========= =================
Key Value Default Description
======= ==================== ========= =================
``t`` Single character. ``a`` The type of drag and drop event.
``(a, A, ``a`` - start accepting drops
)`` ``A`` - stop accepting drops
``m`` - a drop move event
``M`` - a drop dropped event
``r`` - request dropped data
``R`` - report an error
``o`` - start offering drags or start a drag
``p`` - present data for drag offers
``P`` - Change drag image or start drag
``e`` - a drag offer event occurred
``E`` - a drag offer data error occurred
``k`` - data for uri-list items in drag offer
``q`` - query support for this protocol
``m`` Chunking indicator ``0`` ``0`` or ``1``
``i`` Postive integer ``0`` This id is for use by multiplexers.
When it is set, all responses from
the terminal in that session will
have it set to the same value.
``o`` Positive integer ``0`` What drop operation to perform. ``0``
means rejected, ``1`` means copy and
``2`` means move.
**Keys for location**
-----------------------------------------------------------
``x`` Integer ``0`` Cell x-coordinate origin is 0, 0 at top left of screen
``y`` Integer ``0`` Cell y-coordinate origin is 0, 0 at top left of screen
``X`` Integer ``0`` Pixel x-coordinate origin is 0, 0 at top left of screen
``Y`` Integer ``0`` Pixel y-coordinate origin is 0, 0 at top left of screen
======= ==================== ========= =================
.. _machine_id:
Machine id
-----------------
The machine id is used to detect when the source and destination machines for a
drag and drop are different. It is of the form: ``version:ASCII printable
chars``. The leading ``version`` field allows for changing the format or
semantics of this field in the future. The actual id is the machine id which
is:
.. tab:: macOS
The value returned by the ``IOPlatformUUID`` system function.
.. tab:: Windows
The contents of the :file:`HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\MachineGuid`
registry key.
.. tab:: Other
The contents of the :file:`/etc/machine-id` file with trailing whitespace removed
This machine id is then hashed using a :rfc:`HMAC <2104>` with :rfc:`SHA-256
<6234>` as the digest algorithm and the key being the ASCII bytes:
``tty-dnd-protocol-machine-id``. The hashing is done so as to not easily leak
the actual machine id and to ensure that the value is of fixed size and
consisting only of ASCII printable characters. This gives a final value of::
1:hashed machine id hexadecimal encoded
In the future, the ``version`` field may increase if the hashing algorithm is
changed. If the terminal sees a version it does not understand, it must assume
that the machine id does not match, aka the source and destination machines are
different. This assumption means that remote drag and drop will still work, just with
reduced performance in case of version mismatch.
Support in other software
------------------------------
Currently this protocol is supported in:
* The kitty terminal emulator and the :doc:`dnd kitten </kittens/dnd>`
* The `yazi <https://github.com/sxyazi/yazi/pull/4005>`__ terminal file manager

View File

@@ -36,6 +36,78 @@ within a fixed width across all faces of the font, otherwise they aren't really
monospaced.
Using a color theme with a background color does not work well in vim?
-----------------------------------------------------------------------
First, be sure to `use a color scheme in vim <https://github.com/kovidgoyal/kitty/discussions/8196#discussioncomment-11739991>`__
instead of relying on the terminal theme. Otherwise, background and text selection colours
may be difficult to read.
Sadly, vim has very poor out-of-the-box detection for modern terminal features.
Furthermore, it `recently broke detection even more <https://github.com/vim/vim/issues/11729>`__.
It kind of, but not really, supports terminfo, except it overrides it with its own hard-coded
values when it feels like it. Worst of all, it has no ability to detect modern
features not present in terminfo, at all, even security sensitive ones like
bracketed paste.
Thankfully, probably as a consequence of this lack of detection, vim allows users to
configure these low level details. So, to make vim work well with any modern
terminal, including kitty, add the following to your :file:`~/.vimrc`.
.. code-block:: vim
" Mouse support
set mouse=a
set ttymouse=sgr
set balloonevalterm
" Styled and colored underline support
let &t_AU = "\e[58:5:%dm"
let &t_8u = "\e[58:2:%lu:%lu:%lum"
let &t_Us = "\e[4:2m"
let &t_Cs = "\e[4:3m"
let &t_ds = "\e[4:4m"
let &t_Ds = "\e[4:5m"
let &t_Ce = "\e[4:0m"
" Strikethrough
let &t_Ts = "\e[9m"
let &t_Te = "\e[29m"
" Truecolor support
let &t_8f = "\e[38:2:%lu:%lu:%lum"
let &t_8b = "\e[48:2:%lu:%lu:%lum"
let &t_RF = "\e]10;?\e\\"
let &t_RB = "\e]11;?\e\\"
" Bracketed paste
let &t_BE = "\e[?2004h"
let &t_BD = "\e[?2004l"
let &t_PS = "\e[200~"
let &t_PE = "\e[201~"
" Cursor control
let &t_RC = "\e[?12$p"
let &t_SH = "\e[%d q"
let &t_RS = "\eP$q q\e\\"
let &t_SI = "\e[5 q"
let &t_SR = "\e[3 q"
let &t_EI = "\e[1 q"
let &t_VS = "\e[?12l"
" Focus tracking
let &t_fe = "\e[?1004h"
let &t_fd = "\e[?1004l"
execute "set <FocusGained>=\<Esc>[I"
execute "set <FocusLost>=\<Esc>[O"
" Window title
let &t_ST = "\e[22;2t"
let &t_RT = "\e[23;2t"
" vim hardcodes background color erase even if the terminfo file does
" not contain bce. This causes incorrect background rendering when
" using a color theme with a background color in terminals such as
" kitty that do not support background color erase.
let &t_ut=''
These settings must be placed **before** setting the ``colorscheme``. It is
also important that the value of the vim ``term`` variable is not changed
after these settings.
I get errors about the terminal being unknown or opening the terminal failing or functional keys like arrow keys don't work?
-------------------------------------------------------------------------------------------------------------------------------
@@ -134,39 +206,30 @@ remote control command, for details, see :ref:`at-set-colors`.
To change colors when SSHing into a remote host, use the :opt:`color_scheme
<kitten-ssh.color_scheme>` setting for the :doc:`ssh kitten <kittens/ssh>`.
Additionally, you can use the escape code described in :doc:`color-stack`
to set colors in a single window.
Examples of using OSC escape codes to set colors::
Additionally, You can use the
`OSC terminal escape codes <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands>`__
to set colors. Examples of using OSC escape codes to set colors::
Change the default foreground color:
printf '\x1b]21;foreground=#ff0000\x1b\\'
printf '\x1b]10;#ff0000\x1b\\'
Change the default background color:
printf '\x1b]21;background=blue\x1b\\'
printf '\x1b]11;blue\x1b\\'
Change the cursor color:
printf '\x1b]21;cursor=blue\x1b\\'
printf '\x1b]12;blue\x1b\\'
Change the selection background color:
printf '\x1b]21;selection_background=blue\x1b\\'
printf '\x1b]17;blue\x1b\\'
Change the selection foreground color:
printf '\x1b]21;selection_foreground=blue\x1b\\'
printf '\x1b]19;blue\x1b\\'
Change the nth color (0 - 255):
printf '\x1b]21;n=green\x1b\\'
printf '\x1b]4;n;green\x1b\\'
See :doc:`color-stack` for details on the syntax for specifying colors and
how to query current colors.
You can use various syntaxes/names for color specifications in the above
examples. See `XParseColor <https://linux.die.net/man/3/xparsecolor>`__
for full details.
If a ``?`` is given rather than a color specification, kitty will respond
with the current value for the specified color.
Why is there padding between the text area and the window border?
------------------------------------------------------------------
Terminal screens are a grid of fixed size cells. When the window size is
not an exact multiple of the cell size, there is some leftover space that
appears as padding. Additionally you can add your own padding with
:opt:`window_padding_width`. This padding can be particularly noticeable when
using TUI programs that have a background color different from the terminal
background color. In such cases either change their background color to match
the terminal or more correctly, open a bug report against such programs and ask
them to :doc:`change the terminal default background color <color-stack>`
using OSC escape codes when starting and restore it when quitting to have it work automatically.
How do I specify command line options for kitty on macOS?
---------------------------------------------------------------
@@ -220,9 +283,9 @@ fonts to be freely resizable, so it does not support bitmapped fonts.
symbols, so to force kitty to use the pure NERD font for NERD symbols,
add the following line to :file:`kitty.conf`::
# Nerd Fonts v3.4.0
# Nerd Fonts v3.3.0
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
symbol_map U+e000-U+e00a,U+ea60-U+ebeb,U+e0a0-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+ed00-U+efc1,U+f000-U+f2ff,U+f000-U+f2e0,U+f300-U+f381,U+f400-U+f533,U+f0001-U+f1af0 Symbols Nerd Font Mono
Those Unicode symbols not in the `Unicode private use areas
<https://en.wikipedia.org/wiki/Private_Use_Areas>`__ are
@@ -326,10 +389,6 @@ many alternate icons available, click on an icon to visit its homepage:
:target: https://github.com/igrmk/twiskers
:width: 256
.. image:: https://github.com/mtklr/kitty-nyan-icon/raw/main/kitty-nyan.svg
:target: https://github.com/mtklr/kitty-nyan-icon
:width: 256
You can put :file:`kitty.app.icns` (macOS only) or :file:`kitty.app.png` in the
:ref:`kitty configuration directory <confloc>`, and this icon will be applied
automatically at startup. On X11 and Wayland, this will set the icon for kitty windows.
@@ -357,20 +416,13 @@ also set it with the following command:
You can also change the icon manually by following the steps:
.. tab:: macOS
#. Find :file:`kitty.app` in the Applications folder, select it and press :kbd:`⌘+I`
#. Drag :file:`kitty.icns` onto the application icon in the kitty info pane
#. Delete the icon cache and restart Dock:
#. Find :file:`kitty.app` in the Applications folder, select it and press :kbd:`⌘+I`
#. Drag :file:`kitty.icns` onto the application icon in the kitty info pane
#. Delete the icon cache and restart Dock::
.. code-block:: sh
rm /var/folders/*/*/*/com.apple.dock.iconcache; killall Dock
.. tab:: Linux
#. Copy :file:`kitty.desktop` from the installation location (usually
:file:`/usr/share/applications` to :file:`~/.local/share/applications`
#. Edit the copied desktop file changing the ``Icon`` line to have
the absolute path to your desired icon.
rm /var/folders/*/*/*/com.apple.dock.iconcache; killall Dock
How do I map key presses in kitty to different keys in the terminal program?
@@ -421,16 +473,6 @@ related to localization, such as :envvar:`LANG`, ``LC_*`` and loading of
configuration files such as ``XDG_*``, :envvar:`KITTY_CONFIG_DIRECTORY` and,
most importantly, ``PATH`` to locate binaries.
The simplest way to fix this is to have kitty load the environment variables
from your shell configuration at startup using the :opt:`env` directive,
adding the following to :file:`kitty.conf`::
env read_from_shell=PATH LANG LC_* XDG_* EDITOR VISUAL
This works for POSIX compliant shells and the fish shell. Note that it
does add significantly to kitty startup time, so use only if really necessary.
This feature was added in version ``0.43.2``.
To see the environment variables that kitty sees, you can add the following
mapping to :file:`kitty.conf`::
@@ -511,79 +553,6 @@ maintains. These too allocate memory in large blocks and don't release it back
to the OS immediately.
Using a color theme with a background color does not work well in vim?
-----------------------------------------------------------------------
First, be sure to `use a color scheme in vim <https://github.com/kovidgoyal/kitty/discussions/8196#discussioncomment-11739991>`__
instead of relying on the terminal theme. Otherwise, background and text selection colours
may be difficult to read.
Sadly, vim has very poor out-of-the-box detection for modern terminal features.
Furthermore, it `recently broke detection even more <https://github.com/vim/vim/issues/11729>`__.
It kind of, but not really, supports terminfo, except it overrides it with its own hard-coded
values when it feels like it. Worst of all, it has no ability to detect modern
features not present in terminfo, at all, even security sensitive ones like
bracketed paste.
Thankfully, probably as a consequence of this lack of detection, vim allows users to
configure these low level details. So, to make vim work well with any modern
terminal, including kitty, add the following to your :file:`~/.vimrc`.
.. code-block:: vim
" Mouse support
set mouse=a
set ttymouse=sgr
set balloonevalterm
" Styled and colored underline support
let &t_AU = "\e[58:5:%dm"
let &t_8u = "\e[58:2:%lu:%lu:%lum"
let &t_Us = "\e[4:2m"
let &t_Cs = "\e[4:3m"
let &t_ds = "\e[4:4m"
let &t_Ds = "\e[4:5m"
let &t_Ce = "\e[4:0m"
" Strikethrough
let &t_Ts = "\e[9m"
let &t_Te = "\e[29m"
" Truecolor support
let &t_8f = "\e[38:2:%lu:%lu:%lum"
let &t_8b = "\e[48:2:%lu:%lu:%lum"
let &t_RF = "\e]10;?\e\\"
let &t_RB = "\e]11;?\e\\"
" Bracketed paste
let &t_BE = "\e[?2004h"
let &t_BD = "\e[?2004l"
let &t_PS = "\e[200~"
let &t_PE = "\e[201~"
" Cursor control
let &t_RC = "\e[?12$p"
let &t_SH = "\e[%d q"
let &t_RS = "\eP$q q\e\\"
let &t_SI = "\e[5 q"
let &t_SR = "\e[3 q"
let &t_EI = "\e[1 q"
let &t_VS = "\e[?12l"
" Focus tracking
let &t_fe = "\e[?1004h"
let &t_fd = "\e[?1004l"
execute "set <FocusGained>=\<Esc>[I"
execute "set <FocusLost>=\<Esc>[O"
" Window title
let &t_ST = "\e[22;2t"
let &t_RT = "\e[23;2t"
" vim hardcodes background color erase even if the terminfo file does
" not contain bce. This causes incorrect background rendering when
" using a color theme with a background color in terminals such as
" kitty that do not support background color erase.
let &t_ut=''
These settings must be placed **before** setting the ``colorscheme``. It is
also important that the value of the vim ``term`` variable is not changed
after these settings.
Why does kitty sometimes start slowly on my Linux system?
-------------------------------------------------------------------------------------------

View File

@@ -127,7 +127,7 @@ receiving::
The client must then wait for responses from the terminal emulator. It
is an error to send anymore commands to the terminal until an ``OK``
response is received from the terminal. The terminal waits for the user to accept
response is received from the terminal. The terminal wait for the user to accept
the request. If accepted, it sends::
← action=status id=someid status=OK

View File

@@ -224,14 +224,6 @@ Variables that kitty sets when running child programs
Set when enabling :ref:`shell_integration`. It is automatically removed by
the shell integration scripts.
.. envvar:: KITTY_SI_RUN_COMMAND_AT_STARTUP
Set this to an expression that the kitty shell integration scripts will
``eval`` after the shell is started. Note that this environment variable
is ignored when present in the environment in which kitty itself is launched
in. It is most useful with the ``--env`` flag for the :doc:`launch <launch>`
action.
.. envvar:: ZDOTDIR
Set when enabling :ref:`shell_integration` with :program:`zsh`, allowing

View File

@@ -28,15 +28,12 @@ alpha-blending and text over graphics.
Some applications that use the kitty graphics protocol:
* `awrit <https://github.com/chase/awrit>`_ - Chromium-based web browser rendered in Kitty with mouse and keyboard support
* `blackcat <https://github.com/j-c-m/blackcat>`_ - a modern compatible cat with image support
* `bicat <https://github.com/stevenxxiu/bicat>`_ - a terminal image viewer that also works in the Vifm file manager, with nested Tmux support
* `broot <https://dystroy.org/broot/>`_ - a terminal file explorer and manager, with preview of images, SVG, PDF, etc.
* `chafa <https://github.com/hpjansson/chafa>`_ - a terminal image viewer
* :doc:`kitty-diff <kittens/diff>` - a side-by-side terminal diff program with support for images
* `fzf <https://github.com/junegunn/fzf/commit/d8188fce7b7bea982e7f9050c35e488e49fb8fd0>`_ - A command line fuzzy finder
* `mpv <https://github.com/mpv-player/mpv/commit/874e28f4a41a916bb567a882063dd2589e9234e1>`_ - A video player that can play videos in the terminal
* `neofetch <https://github.com/dylanaraps/neofetch>`_ - A command line system information tool
* `nvim <https://github.com/neovim/neovim/issues/30889>`__ - A TUI editor that can display images in the terminal
* `pixcat <https://github.com/mirukana/pixcat>`_ - a third party CLI and python library that wraps the graphics protocol
* `ranger <https://github.com/ranger/ranger>`_ - a terminal file manager, with image previews
* `termpdf.py <https://github.com/dsanson/termpdf.py>`_ - a terminal PDF/DJVU/CBR viewer
@@ -56,7 +53,6 @@ Libraries:
* `image.nvim <https://github.com/3rd/image.nvim>`_ - Bringing images to neovim
* `image_preview.nvim <https://github.com/adelarsq/image_preview.nvim/>`_ - Image preview for neovim
* `kui.nvim <https://github.com/romgrk/kui.nvim>`_ - Build sophisticated UIs inside neovim using the kitty graphics protocol
* `kitty-graphics.el <https://github.com/cashmeredev/kitty-graphics.el>`_ - Images in emacs
* `term-image <https://github.com/AnonymouX47/term-image>`_ - A Python library, CLI and TUI to display and browse images in the terminal
* `glkitty <https://github.com/michaeljclark/glkitty>`_ - C library to draw OpenGL shaders in the terminal with a glgears demo
@@ -68,8 +64,6 @@ Other terminals that have implemented the graphics protocol:
* `Warp <https://docs.warp.dev/getting-started/changelog#id-2025.03.26-v0.2025.03.26.08.10>`_
* `wayst <https://github.com/91861/wayst>`_
* `WezTerm <https://github.com/wez/wezterm/issues/986>`_
* `iTerm2 <https://github.com/gnachman/iTerm2/commit/4fe5b2173193b6c3e45234b6b2ab7a144a5cfa01>`_
* `xterm.js <https://github.com/xtermjs/xterm.js/discussions/5683>`_
Getting the window size
@@ -106,7 +100,7 @@ code to demonstrate its use
buf = array.array('H', [0, 0, 0, 0])
fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, buf)
print((
'number of rows: {} number of columns: {} '
'number of rows: {} number of columns: {}'
'screen width: {} screen height: {}').format(*buf))
.. tab:: Go
@@ -137,30 +131,19 @@ code to demonstrate its use
}
.. tab:: POSIX sh
.. tab:: Bash
.. code-block:: sh
#!/bin/sh
#!/bin/bash
read rows cols <<EOF
$(command stty size)
EOF
# This uses the kitten standalone binary from kitty to get the pixel sizes
# since we can't do IOCTLs directly. Fortunately, kitten is a static exe
# pre-built for every Unix like OS under the sun.
oldstty=$(command stty -g)
command stty raw -echo
printf "\033[14t"
response=""
while : ; do
char=$(command dd bs=1 count=1 2>/dev/null)
[ "$char" = "t" ] && break
response="${response}${char}"
done
command stty "$oldstty"
h=$(echo "$response" | cut -d';' -f2)
w=$(echo "$response" | cut -d';' -f3)
printf "number of rows: %d number of columns: %d" "$rows" "$cols"
printf " screen width: %d screen height: %d\n" "$w" "$h"
builtin read -r rows cols < <(command stty size)
IFS=x builtin read -r width height < <(command kitten icat --print-window-size); builtin unset IFS
builtin echo "number of rows: $rows number of columns: $cols screen width: $width screen height: $height"
Note that some terminals return ``0`` for the width and height values. Such
@@ -171,9 +154,7 @@ You can also use the *CSI t* escape code to get the screen size. Send
``<ESC>[14t`` to ``STDOUT`` and kitty will reply on ``STDIN`` with
``<ESC>[4;<height>;<width>t`` where ``height`` and ``width`` are the window
size in pixels. This escape code is supported in many terminals, not just
kitty. A more precise version of this escape code, which is however supported
in less terminals is ``<ESC>[16t`` which causes the terminal to reply with the
pixel dimensions of a single cell.
kitty.
A minimal example
------------------
@@ -181,55 +162,61 @@ A minimal example
Some minimal code to display PNG images in kitty, using the most basic
features of the graphics protocol:
.. tab:: POSIX sh
.. tab:: Bash
.. code-block:: sh
#!/bin/sh
send_chunked() {
first="y"
while IFS= read -r chunk; do
metadata=""; [ "$first" = "y" ] && { metadata="a=T,f=100,"; first="n"; }
printf "\033_G%sm=1;%s\033\\" "${metadata}" "${chunk}"
done
[ "$first" = "n" ] && { printf "\033_Gm=0;\033\\"; return 0; }
return 1
}
#!/bin/bash
transmit_png() {
# Different systems have different or missing base64 executables.
# The sed command below adds a trailing newline which openssl
# base64 does not produce and is needed for reading via read -r
{ command base64 -w 4096 "$1" 2>/dev/null | send_chunked; } || \
{ command base64 -b 4096 "$1" 2>/dev/null | send_chunked; } || \
{ command openssl base64 -e -A -in "$1" | command sed '$a\' | command fold -b -w 4096 | send_chunked; }
data=$(base64 "$1")
data="${data//[[:space:]]}"
builtin local pos=0
builtin local chunk_size=4096
while [ $pos -lt ${#data} ]; do
builtin printf "\e_G"
[ $pos = "0" ] && printf "a=T,f=100,"
builtin local chunk="${data:$pos:$chunk_size}"
pos=$(($pos+$chunk_size))
[ $pos -lt ${#data} ] && builtin printf "m=1"
[ ${#chunk} -gt 0 ] && builtin printf ";%s" "${chunk}"
builtin printf "\e\\"
done
}
transmit_png "$1"
.. tab:: Python
.. code-block:: python
#!/usr/bin/env python
#!/usr/bin/python
import sys
from base64 import standard_b64encode
first, eof, buf = True, False, memoryview(bytearray(3 * 4096 // 4))
w = sys.stdout.buffer.write
def serialize_gr_command(**cmd):
payload = cmd.pop('payload', None)
cmd = ','.join(f'{k}={v}' for k, v in cmd.items())
ans = []
w = ans.append
w(b'\033_G'), w(cmd.encode('ascii'))
if payload:
w(b';')
w(payload)
w(b'\033\\')
return b''.join(ans)
def write_chunked(**cmd):
data = standard_b64encode(cmd.pop('data'))
while data:
chunk, data = data[:4096], data[4096:]
m = 1 if data else 0
sys.stdout.buffer.write(serialize_gr_command(payload=chunk, m=m,
**cmd))
sys.stdout.flush()
cmd.clear()
with open(sys.argv[-1], 'rb') as f:
while not eof:
p = buf[:]
while p and not eof:
n = f.readinto1(p)
p, eof = p[n:], n == 0
encoded = standard_b64encode(buf[:len(buf)-len(p)])
metadata, first = "a=T,f=100," if first else "", False
w(f'\x1b_G{metadata}m={0 if eof else 1};'.encode('ascii'))
w(encoded)
w(b'\x1b\\')
write_chunked(a='T', f=100, data=f.read())
Save this script as :file:`send-png`, then you can use it to display any PNG
@@ -350,7 +337,7 @@ similar to reporting any other kind of I/O error. Since the file paths come
from potentially untrusted sources, terminal emulators **must** refuse to read
any device/socket/etc. special files. Only regular files are allowed.
Additionally, terminal emulators may refuse to read files in *sensitive*
parts of the filesystem, such as :file:`/proc`, :file:`/sys`, :file:`/dev`, etc.
parts of the filesystem, such as :file:`/proc`, :file:`/sys`, :file:`/dev/`, etc.
Local client
^^^^^^^^^^^^^^
@@ -368,7 +355,7 @@ Here we tell the terminal emulator to read compressed image data from
the specified shared memory object.
The client can also specify a size and offset to tell the terminal emulator
to only read a part of the specified file. This is done using the ``S`` and ``O``
to only read a part of the specified file. The is done using the ``S`` and ``O``
keys respectively. For example::
<ESC>_Gs=10,v=2,t=s,S=80,O=10;<encoded /some-shared-memory-name><ESC>\
@@ -480,11 +467,10 @@ id. To do so add the ``p`` key with a number between ``1`` and ``4294967295``.
When you specify a placement id, it will be added to the acknowledgement code
above. Every placement is uniquely identified by the pair of the ``image id``
and the ``placement id``. If you specify a placement id for an image that does
not have an id (i.e. has id=0), it will be ignored, i.e. the placement will not
get an id. In particular this means there can exist multiple images with
``image id=0, placement id=0``. Not specifying a placement id or using ``p=0``
for multiple put commands (``a=p``) with the same non-zero image id results in
multiple placements the image.
not have an id (i.e. has id=0), it will be ignored. In particular this means
there can exist multiple images with ``image id=0, placement id=0``. Not
specifying a placement id or using ``p=0`` for multiple put commands (``a=p``)
with the same non-zero image id results in multiple placements the image.
An example response::
@@ -495,14 +481,6 @@ second one will replace the first. This can be used to resize or move
placements around the screen, without flicker.
.. note::
When re-transmitting image data for a specific id, the existing image and
all its placements must be deleted. The new data replaces the old image data
but is not actually displayed until a placement for it is created. This is
to avoid divergent behavior in the case when unrelated programs happen to re-use
image ids in the same session.
.. versionadded:: 0.19.3
Support for specifying placement ids (see :doc:`kittens/query_terminal` to query kitty version)
@@ -834,27 +812,6 @@ use the ``i`` key with the image id for all future communication.
The ability to use image numbers (see :doc:`kittens/query_terminal` to query kitty version)
.. _image_usage_hints:
Usage hints
-----------------------------
Clients can specify *usage hints* when creating images using the ``N`` key.
These hints allow the terminal to optimise resource consumption such as caching
strategies. The value of ``N`` is a bitmask.
Currently the only usage hint defined is ``transient (N == 1)``.
The terminal is free to assume that an image with this hint
will be used for only a short time, and so may, for example, evict its
data before other images when the image is soft deleted, has no visible
placements and the terminal is under storage pressure, or skip writing
its data to disk. The terminal is also free to ignore the hint. If an
animation frame with the *transient* hint is composited onto another
frame, and any of the involved frames have the hint, the resulting
composited frame also has the hint. This hint must be specified when the
image or frame data is transmitted. It has no effect on placement commands.
.. _animation_protocol:
Animation
@@ -929,12 +886,12 @@ on.
Finally, while transferring frame data, the frame *gap* can also be specified
using the ``z`` key. The gap is the number of milliseconds to wait before
displaying the next frame when the animation is running. A value of ``z=0`` is
ignored (acts as though ``z`` was unspecified), ``z=positive number`` sets the
gap to the specified number of milliseconds and ``z=negative number`` creates a
*gapless* frame. Gapless frames are not displayed to the user since they are
instantly skipped over, however they can be useful as the base data for
subsequent frames. For example, for an animation where the background remains
the same and a small object or two move.
ignored, ``z=positive number`` sets the gap to the specified number of
milliseconds and ``z=negative number`` creates a *gapless* frame. Gapless
frames are not displayed to the user since they are instantly skipped over,
however they can be useful as the base data for subsequent frames. For example,
for an animation where the background remains the same and a small object or two
move.
Controlling animations
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -964,9 +921,9 @@ animation. ``s=2`` runs the animation, but in *loading* mode, in this mode when
reaching the last frame, instead of looping, the terminal will wait for the
arrival of more frames. ``s=3`` runs the animation normally, after the last
frame, the terminal loops back to the first frame. The number of loops can be
controlled by the ``v`` key. ``v=0`` is ignored (acts as though ``v`` was not
specified), ``v=1`` is loop infinitely, and any other positive number is loop
``number - 1`` times. Note that stopping the animation resets the loop counter.
controlled by the ``v`` key. ``v=0`` is ignored, ``v=1`` is loop infinitely,
and any other positive number is loop ``number - 1`` times. Note that stopping
the animation resets the loop counter.
Finally, the *gap* for frames can be set using the ``z`` key. This can be
specified either when the frame is created as part of the transmit escape code
@@ -1072,8 +1029,7 @@ Key Value Default Description
``o`` Single character. ``null`` The type of data compression.
``only z``
``m`` zero or one ``0`` Whether there is more chunked data available.
``N`` bitmask ``0`` Usage hints from the client to the terminal about the intended use of
the image.
**Keys for image display**
-----------------------------------------------------------
``x`` Positive integer ``0`` The left edge (in pixels) of the image area to display

View File

@@ -1,9 +1,7 @@
kitty
==========================================================
*If you live in the terminal, kitty is made for YOU!*
The fast, feature-rich, GPU based terminal emulator.
*The fast, feature-rich, GPU based terminal emulator*
.. toctree::
:hidden:
@@ -12,7 +10,6 @@ The fast, feature-rich, GPU based terminal emulator.
overview
faq
support
sessions
performance
changelog
integrations
@@ -22,7 +19,7 @@ The fast, feature-rich, GPU based terminal emulator.
.. tab:: Fast
* Uses GPU and SIMD vector CPU instructions for :doc:`best in class performance <performance>`
* Uses GPU and SIMD vector CPU instructions for :doc:`best in class <performance>`
* Uses threaded rendering for :iss:`absolutely minimal latency <2701#issuecomment-636497270>`
* Performance tradeoffs can be :ref:`tuned <conf-kitty-performance>`

View File

@@ -14,12 +14,6 @@ Image and document viewers
Powered by kitty's :doc:`graphics-protocol` there exist many tools for viewing
images and other types of documents directly in your terminal, even over SSH.
.. _tool_bookokrat:
`bookokrat <https://github.com/bugzmanov/bookokrat>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A terminal PDF/EPUB viewer
.. _tool_termpdf:
`termpdf.py <https://github.com/dsanson/termpdf.py>`_
@@ -50,11 +44,40 @@ A terminal PDF viewer with GUI-like usage and Vim-like keybindings written in Ru
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Display various types of files nicely formatted with images in the terminal
`dawn <https://github.com/andrewmd5/dawn>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. _tool_ranger:
A markdown editor that uses the text-sizing protocol for large headings and
the graphics protocol for images.
`ranger <https://github.com/ranger/ranger>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A terminal file manager, with previews of file contents powered by kitty's
graphics protocol.
.. _tool_nnn:
`nnn <https://github.com/jarun/nnn/>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Another terminal file manager, with previews of file contents powered by kitty's
graphics protocol.
.. _tool_yazi:
`Yazi <https://github.com/sxyazi/yazi>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Blazing fast terminal file manager, with built-in kitty graphics protocol support
(implemented both Classic protocol and Unicode placeholders).
.. _tool_clifm:
`clifm <https://github.com/leo-arch/clifm>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The shell-like, command line terminal file manager, uses the kitty graphics and
keyboard protocols.
.. _tool_hunter:
`hunter <https://github.com/rabite0/hunter>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Another terminal file manager, with previews of file contents powered by kitty's
graphics protocol.
.. _tool_presentterm:
@@ -63,18 +86,6 @@ the graphics protocol for images.
Show markdown based slides with images in your terminal, powered by the
kitty graphics protocol.
.. _tool_mdfried:
`mdfried <https://github.com/benjajaja/mdfried>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Markdown viewer that can render big headers with the text-sizing-protocol, and
also render images with the kitty graphics protocol.
`kmv <https://github.com/parf/Kitty-Markdown-Viewer>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Markdown viewer that can render big headers with the text-sizing-protocol, and
also render images with the kitty graphics protocol.
.. _tool_term_image:
`term-image <https://github.com/AnonymouX47/term-image>`__
@@ -128,10 +139,6 @@ images.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A video player that can play videos in the terminal.
.. code-block:: sh
mpv --profile=sw-fast --vo=kitty --vo-kitty-use-shm=yes --really-quiet video.mkv
.. _tool_timg:
`timg <https://github.com/hzeller/timg>`_
@@ -141,52 +148,6 @@ plays videos. Fast multi-threaded loading, JPEG exif rotation, grid view and
connecting to the webcam make it a versatile terminal utility.
File managers
-------------------
.. _tool_ranger:
`ranger <https://github.com/ranger/ranger>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A terminal file manager, with previews of file contents powered by kitty's
graphics protocol.
.. _tool_nnn:
`nnn <https://github.com/jarun/nnn/>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Another terminal file manager, with previews of file contents powered by kitty's
graphics protocol.
.. _tool_yazi:
`Yazi <https://github.com/sxyazi/yazi>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Blazing fast terminal file manager, with built-in kitty graphics protocol support
(implemented both Classic protocol and Unicode placeholders).
.. _tool_clifm:
`clifm <https://github.com/leo-arch/clifm>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The shell-like, command line terminal file manager, uses the kitty graphics and
keyboard protocols.
.. _tool_hunter:
`hunter <https://github.com/rabite0/hunter>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Another terminal file manager, with previews of file contents powered by kitty's
graphics protocol.
.. _tool_far2l:
`far2l <https://github.com/elfmz/far2l>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Text-mode dual panel (orthodox) file manager and also terminal emulator, uses
the kitty graphics and keyboard protocols (both as client and as terminal)
System and data visualisation tools
---------------------------------------
@@ -225,8 +186,28 @@ by kitty's graphics protocol for displaying plots
`gnuplot <http://www.gnuplot.info/>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A graphing and data visualization tool that has support for the kitty graphics
protocol, with its ``kittygd`` and ``kittycairo`` backends.
A graphing and data visualization tool that can be made to display its output in
kitty with the following bash snippet:
.. code-block:: sh
function iplot {
cat <<EOF | gnuplot
set terminal pngcairo enhanced font 'Fira Sans,10'
set autoscale
set samples 1000
set output '|kitten icat --stdin yes'
set object 1 rectangle from screen 0,0 to screen 1,1 fillcolor rgb"#fdf6e3" behind
plot $@
set output '/dev/null'
EOF
}
Add this to bashrc and then to plot a function, simply do:
.. code-block:: sh
iplot 'sin(x*3)*exp(x*.2)'
.. _tool_k-nine:
@@ -273,7 +254,6 @@ View and manage the system clipboard under Wayland in your kitty terminal
TUI for QEMU used to manage virtual machines, can display the Virtual Machine
in the terminal using the kitty graphics protocol.
Editor integration
-----------------------
@@ -287,14 +267,6 @@ features such a split windows, previews, REPLs etc.
Integrates with kitty to use native kitty windows for its windows/panels and
REPLs.
.. _tool_orbiton:
`orbiton <https://github.com/xyproto/orbiton/>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A terminal text editor that uses kitty's graphics protocol to render Markdown
documents with fonts and images inline (use the ``-B`` flag for a word
processor-like book mode), and to preview images in its built-in file browser.
.. tool_vim_slime:
`vim-slime <https://github.com/jpalardy/vim-slime#kitty>`_
@@ -322,18 +294,10 @@ Various image viewing plugins for editors
* `image.nvim <https://github.com/3rd/image.nvim>`_ - Bringing images to neovim
* `image_preview.nvim <https://github.com/adelarsq/image_preview.nvim/>`_ - Image preview for neovim
* `hologram.nvim <https://github.com/edluffy/hologram.nvim>`_ - view images inside nvim
* `kitty-graphics.el <https://github.com/cashmeredev/kitty-graphics.el>`_ - view images in emacs
Scrollback manipulation
-------------------------
.. tool_kitty_search_incremental:
`kitty-search-incremental <https://github.com/Mobinshahidi/kitty-search>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Incremental scrollback search with live results, regex and literal modes,
case-sensitivity toggle, match counter, and auto-scroll to the matching line.
.. tool_kitty_scrollback_nvim:
`kitty-scrollback.nvim <https://github.com/mikesmithgh/kitty-scrollback.nvim>`_
@@ -365,30 +329,9 @@ A system panel for Kitty terminal that displays real-time system metrics using t
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A kitten-panel based desktop panel for your desktop
Password managers
---------------------
`1password <https://github.com/mm-zacharydavison/kitty-kitten-1password>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Allow injecting passwords from 1Password into kitty.
`BitWarden <https://github.com/dnanhkhoa/kitty-password-manager>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Inject passwords from BitWarden into kitty
Miscellaneous
------------------
.. tool_doom:
DOOM
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Play the classic shooter DOOM in `kitty <https://github.com/cryptocode/terminal-doom>`__ or even inside `neovim inside kitty
<https://github.com/seandewar/actually-doom.nvim>`__.
.. tool_gattino:
`gattino <https://github.com/salvozappa/gattino>`__

View File

@@ -35,15 +35,11 @@ inside the kitty terminal to report key events.
In addition to kitty, this protocol is also implemented in:
* The `alacritty terminal <https://github.com/alacritty/alacritty/pull/7125>`__
* The `foot terminal <https://codeberg.org/dnkl/foot/issues/319>`__
* The `ghostty terminal <https://ghostty.org>`__
* The `foot terminal <https://codeberg.org/dnkl/foot/issues/319>`__
* The `iTerm2 terminal <https://gitlab.com/gnachman/iterm2/-/issues/10017>`__
* The `Microsoft terminal <https://github.com/microsoft/terminal/pull/19817>`__
* The `rio terminal <https://github.com/raphamorim/rio/commit/cd463ca37677a0fc48daa8795ea46dadc92b1e95>`__
* The `TuiOS terminal (multiplexer) <https://github.com/Gaurav-Gosain/tuios/issues/26>`__
* The `Warp terminal <https://github.com/warpdotdev/Warp/issues/8462#issuecomment-3857779488>`__
* The `WezTerm terminal <https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html>`__
* The `xterm.js terminal <https://github.com/xtermjs/xterm.js/pull/5600>`__
Libraries implementing this protocol:
@@ -51,9 +47,6 @@ Libraries implementing this protocol:
* The `crossterm library <https://github.com/crossterm-rs/crossterm/pull/688>`__
* The `textual library <https://github.com/Textualize/textual/pull/4631>`__
* The vaxis library `go <https://sr.ht/~rockorager/vaxis/>`__ and `zig <https://github.com/rockorager/libvaxis/>`__
* The `bubbletea library <https://github.com/charmbracelet/bubbletea/issues/869>`__
* The `vtinput <https://unxed.github.com/vtinput>`__ and `vtui <https://github.com/unxed/vtui>`__ libraries
* The `tcell library <https://github.com/gdamore/tcell/commit/c10909b991eb87c009554fe9b2dfa7276e2649c1>`__
Programs implementing this protocol:
@@ -63,8 +56,7 @@ Programs implementing this protocol:
* The `kakoune text editor <https://github.com/mawww/kakoune/issues/4103>`__
* The `dte text editor <https://gitlab.com/craigbarnes/dte/-/issues/138>`__
* The `Helix text editor <https://github.com/helix-editor/helix/pull/4939>`__
* The `Flow control editor <https://github.com/neurocyte/flow?tab=readme-ov-file#requirements>`__
* The `far2l <https://github.com/elfmz/far2l/commit/e1f2ee0ef2b8332e5fa3ad7f2e4afefe7c96fc3b>`__ and `f4 <https://github.com/unxed/f4>`__ file managers
* The `far2l file manager <https://github.com/elfmz/far2l/commit/e1f2ee0ef2b8332e5fa3ad7f2e4afefe7c96fc3b>`__
* The `Yazi file manager <https://github.com/sxyazi/yazi>`__
* The `awrit web browser <https://github.com/chase/awrit>`__
* The `Turbo Vision <https://github.com/magiblot/tvision/commit/6e5a7b46c6634079feb2ac98f0b890bbed59f1ba>`__/`Free Vision <https://gitlab.com/freepascal.org/fpc/source/-/issues/40673#note_2061428120>`__ IDEs
@@ -249,19 +241,13 @@ The terminal can optionally send the text associated with key events as a
sequence of Unicode code points. This behavior is opt-in by the :ref:`progressive
enhancement <progressive_enhancement>` mechanism described below. Some examples::
shift+a -> CSI 97 ; 2 ; 65 u # The text 'A' is reported as 65
alt+a -> CSI 0 ; ; 229 u # The text 'å' is reported as 229
shift+a -> CSI 97 ; 2 ; 65 u # The text 'A' is reported as 65
option+a -> CSI 97 ; ; 229 u # The text 'å' is reported as 229
If multiple code points are present, they must be separated by colons. If no
known key is associated with the text the key number ``0`` must be used. The
associated text must not contain control codes (control codes are code points
below U+0020 and codepoints in the C0 and C1 blocks). In the above example, the
:kbd:`alt` modifier is consumed by the OS itself to produce the text å and not
sent to the terminal emulator, which gets only a "text input" event and no
information about modifiers, thus the event gets encoded with no modifiers.
The exact behavior in these situations depends on the OS, keyboard layout, IME
system in use and so on. In general, if the terminal emulator receives no key
information, the key number 0 must be used to indicate a pure "text event".
below U+0020 and codepoints in the C0 and C1 blocks).
Non-Unicode keys
@@ -368,7 +354,7 @@ are the :kbd:`Enter`, :kbd:`Tab` and :kbd:`Backspace` keys which still generate
bytes as in legacy mode this is to allow the user to type and execute commands
in the shell such as ``reset`` after a program that sets this mode crashes
without clearing it. Note that the Lock modifiers are not reported for text
producing keys, to keep them usable in legacy programs. To get lock modifiers
producing keys, to keep them useable in legacy programs. To get lock modifiers
for all keys use the :ref:`report_all_keys` enhancement.
.. _report_events:
@@ -478,10 +464,7 @@ These keys are encoded using three schemes::
In the above, if there are no modifiers, the modifier parameter is omitted.
The modifier value is encoded as described in the :ref:`modifiers` section,
above, except that lock keys (such as :kbd:`Num lock` and :kbd:`Caps lock`)
are not encoded as the legacy mode has no encoding for them.
When the second form is used, the number is always ``1`` and must be
above. When the second form is used, the number is always ``1`` and must be
omitted if the modifiers field is also absent. The third form becomes the
second form when modifiers are present (``SS3 is the bytes 0x1b 0x4f``).
@@ -572,8 +555,8 @@ Any other combination of modifiers with these keys is output as the appropriate
.. csv-table:: Example encodings
:header: "Key", "Plain", "shift", "alt", "ctrl", "shift+alt", "alt+ctrl", "ctrl+shift"
"i", "i (105)", "I (73)", "ESC i", "\t (9)", "ESC I", "ESC \t", "CSI 105; 6 u"
"3", "3 (51)", "# (35)", "ESC 3", "ESC (27)", "ESC #", "ESC ESC", "CSI 51; 6 u"
"i", "i (105)", "I (73)", "ESC i", ") (41)", "ESC I", "ESC )", "CSI 105; 6 u"
"3", "3 (51)", "# (35)", "ESC 3", "3 (51)", "ESC #", "ESC 3", "CSI 51; 6 u"
";", "; (59)", ": (58)", "ESC ;", "; (59)", "ESC :", "ESC ;", "CSI 59; 6 u"
.. note::

View File

@@ -1,136 +0,0 @@
Selecting files, fast
========================
.. only:: man
Overview
--------------
.. versionadded:: 0.45.0
.. only:: not man
.. figure:: /screenshots/choose-files.webp
:alt: The choose files kitten, showing metadata and title from an e-book file
:align: center
:width: 100%
The choose-files kitten is designed to allow you to select files, very fast,
with just a few key strokes. It operates like `fzf
<https://github.com/junegunn/fzf/>`__ and similar fuzzy finders, except that
it is specialised for finding files. As such it supports features such as
filtering by file type, file type icons, content previews and
so on, out of the box. It can be used as a drop in (but much more efficient and
keyboard friendly) replacement for the :guilabel:`File open and save`
dialog boxes common to GUI programs. On Linux, with the help of the
:doc:`desktop-ui </kittens/desktop-ui>` kitten, you can even convince
most GUI programs on your computer to use this kitten instead of regular file
dialogs.
Simply run it as::
kitten choose-files
to select a single file from the tree rooted at the current working directory.
Type a few letters from the filename and once it becomes the top selection,
press :kbd:`Enter`. You can change the current directory by selecting a
directory and pressing the :kbd:`Tab` key. :kbd:`Shift+Tab` goes up one
directory level.
If you want to choose a file and insert it into your shell prompt at the
current cursor position, press :sc:`insert_chosen_file` for files or
:sc:`insert_chosen_directory` for directories. Similarly, to have a file
chosen in a command line, use, for example::
some-command $(kitten choose-file)
Note that the above may not work in a complicated pipeline as it performs
terminal I/O and needs exclusive access to the tty device while choosing a
file.
.. note:: For content previews, this kitten uses some external programs. In
particular `ffmpeg <https://www.ffmpeg.org/>`__ is needed for video
previews and `calibre <https://calibre-ebook.com>`__ is needed
for ebook metadata and cover preiews.
Creating shortcuts to favorite/frequently used directories
------------------------------------------------------------
You can create keyboard shortcuts to quickly switch to any directory in
:file:`choose-files.conf`. For example:
.. code-block:: conf
map ctrl+t cd /tmp
map alt+p cd ~/my/project
Selecting multiple files
-----------------------------
When you wish to select multiple files, start the kitten with :option:`--mode
<kitty +kitten choose_files --mode>`:code:`=files`. Then instead of pressing
:kbd:`Enter`, press :kbd:`Shift+Enter` instead and the file will be added to the list
of selections. You can also hold the :kbd:`Ctrl` key and click on files to add
them to the selections. Similarly, you can hold the :kbd:`Alt` key and click to
select ranges of files (similar to using :kbd:`Shift+click` in a GUI app).
Press :kbd:`Enter` on the last selected file to finish. The list of selected
files is displayed at the bottom of the kitten and you can click on them
to deselect a file. Similarly, pressing :kbd:`Shift+Enter` will un-select a
previously selected file.
Hidden and ignored files
--------------------------
By default, the kitten does not process hidden files and directories (whose
names start with a period). This can be :opt:`changed in the configuration <kitten-choose_files.show_hidden>`
and also at runtime via the clickable link to the right of the search input.
Similarly, the kitten respects both :file:`.gitignore` and :file:`.ignore`
files, by default. This can also be changed both :opt:`in configuration
<kitten-choose_files.respect_ignores>` or at runtime. Note that
:file:`.gitignore` files are only respected if there is also a :file:`.git`
directory present. The kitten also supports the global :file:`.gitignore` file,
though it applies only inside git working trees. You can specify :opt:`global ignore
patterns <kitten-choose_files.ignore>`, that apply everywhere in :file:`choose-files.conf`.
Selecting non-existent files (save file names)
-------------------------------------------------
This kitten can also be used to select non-existent files, that is a new file
for a :guilabel:`Save file` type of dialog using :option:`--mode <kitty +kitten
choose_files --mode>`:code:`=save-file`. Once you have changed to the directory
you want the file to be in (using the :kbd:`Tab` key),
press :kbd:`Ctrl+Enter` and you will be able to type in the file name. If you
wish to modify an existing file name use :kbd:`Alt+Enter` to modify the
filename of the current top match instead.
Selecting directories
---------------------------
This kitten can also be used to select directories,
for an :guilabel:`Open directory` type of dialog using :option:`--mode <kitty +kitten
choose_files --mode>`:code:`=dir`. Once you have changed to the directory
you want, press :kbd:`Ctrl+Enter` to accept it. Or if you are in a parent
directory you can select a descendant directory by pressing :kbd:`Enter`, the
same as you would for selecting a file to open.
Configuration
------------------------
You can configure various aspects of the kitten's operation by creating a
:file:`choose-files.conf` in your :ref:`kitty config folder <confloc>`.
See below for the supported configuration directives.
.. include:: /generated/conf-kitten-choose_files.rst
.. include:: /generated/cli-kitten-choose_files.rst

View File

@@ -118,7 +118,7 @@ that is, they are used to set the variable value for some font characteristic.
<https://harfbuzz.github.io/harfbuzz-hb-common.html#hb-feature-from-string>`__
``system``
This can be used to pass an arbitrary string, usually a family or full name
This can be used to pass an arbitrary string, usuall a family or full name
to the OS font selection APIs. Should not be used in conjunction with any
other keys. Is the same as specifying just the font name without any keys.

View File

@@ -6,7 +6,7 @@ clipboard
Overview
--------------
*Copy/paste to the system clipboard from shell scripts*
*Copy/paste to the system clipboard from shell scripts*
.. highlight:: sh

View File

@@ -1,142 +0,0 @@
Command palette
=================
.. only:: man
Overview
--------------
The command palette lets you browse, search and trigger all keyboard shortcuts
and actions in |kitty| from a single searchable overlay. Press
:sc:`command_palette` to open it (default: :kbd:`Ctrl+Shift+F3`).
.. figure:: ../screenshots/command-palette.webp
:alt: A screenshot of the command palette kitten
:align: center
:width: 100%
The command palette showing search results for ``win close``.
All mapped actions (those with a keyboard shortcut) and unmapped actions (those
available but not bound to any key) are listed, organized by category. Mouse
bindings are shown in a separate section. Simply type to search, select a
result, and press :kbd:`Enter` to run it.
Searching
-----------
As you type into the search bar, the palette filters results in real time.
Matching is case-insensitive and works across three columns simultaneously:
the **key** (keyboard shortcut), the **action** name, and the **category**.
Matched characters are highlighted so you can see exactly where each term hit.
**Multiple words**
Separate your terms with spaces. Typing ``scroll page`` looks for items
that contain *scroll* and *page* anywhere across the key, action, or
category columns. Items that match more of your terms are ranked higher
than those that match fewer.
**How individual words are matched**
Each word in your query is compared against every word in the three
columns. The best match wins and determines the item's score for that
term:
- *Exact word* — the query word equals a column word exactly (highest score).
- *Prefix* — the column word starts with the query word,
e.g. ``scr`` matches ``scroll``.
- *Typo tolerance* — for words of four characters or longer, a single
typo (one character inserted, deleted, or substituted) still produces a
match, and two typos give a lower-scoring match.
An item appears in the results as long as at least one query word matches
something. Items where more query words match rank above those where fewer
match.
**Compound names**
Delimiters such as ``_``, ``+``, ``/``, and ``-`` are kept intact inside a
query word, so you can search for compound action names as a unit.
Typing ``mouse_selection`` first tries to find that exact substring in each
column. If that fails, it splits the token into its parts (``mouse`` and
``selection``) and matches each part independently against the column words.
**Ranking**
When multiple items match the same query, they are sorted by:
1. Number of query words that matched (more is better).
2. Score on the action column (action matches outrank key or category matches).
3. Score on the key column.
4. Score on the category column.
5. Shorter action name as a tiebreaker (more specific results first).
Keyboard controls
-------------------
The following keys are available while the command palette is open:
.. list-table::
:widths: auto
:header-rows: 1
* - Key
- Action
* - Any text
- Filter results by typing a search query
* - :kbd:`Enter`
- Run the selected action
* - :kbd:`Escape`
- Clear the search query, or close the palette if the query is already empty
* - :kbd:`Up` / :kbd:`Ctrl+K` / :kbd:`Ctrl+P`
- Move selection up
* - :kbd:`Down` / :kbd:`Ctrl+J` / :kbd:`Ctrl+N`
- Move selection down
* - :kbd:`Page Up`
- Move selection up by a page
* - :kbd:`Page Down`
- Move selection down by a page
* - :kbd:`Home`
- Jump to the first result
* - :kbd:`End`
- Jump to the last result
* - :kbd:`Backspace`
- Delete the last character from the query
* - :kbd:`F12`
- Toggle display of unmapped actions
* - Mouse click
- Select and run the clicked action
Unmapped actions
------------------
By default, the palette shows both mapped actions (those bound to a shortcut)
and unmapped actions (those with no shortcut assigned). Unmapped actions appear
with an ``(unmapped)`` label in the key column. Press :kbd:`F12` to toggle
their visibility. This preference is remembered across sessions.
Unmapped actions are useful for discovering functionality that you may not have
configured a shortcut for. You can run them directly from the palette, or note
the action name and add a mapping in :file:`kitty.conf`.
Custom keyboard modes
-----------------------
If you have defined custom :ref:`keyboard modes <modal_mappings>` in your
configuration, their bindings appear under separate mode headers in the palette.
The ``push_keyboard_mode`` bindings are grouped with the target mode they
activate, making it easy to see how to enter each mode alongside its shortcuts.
Configuration
--------------
The default mapping to open the command palette is::
map kitty_mod+f3 command_palette
You can change this in :file:`kitty.conf` like any other mapping. For example::
map ctrl+p command_palette

View File

@@ -122,9 +122,9 @@ the table below:
.. table:: Types of input to kittens
:align: left
=========================== ========================================================================================
=========================== =======================================================================================================
Keyword Type of :file:`STDIN` input
=========================== ========================================================================================
=========================== =======================================================================================================
``text`` Plain text of active window
``ansi`` Formatted text of active window
``screen`` Plain text of active window with line wrap markers
@@ -141,7 +141,7 @@ the table below:
``output-screen-ansi`` Formatted text of the output from the last run command with wrap markers
``selection`` The text currently selected with the mouse
=========================== ========================================================================================
=========================== =======================================================================================================
In addition to ``output``, that gets the output of the last run command,
``last_visited_output`` gives the output of the command last jumped to
@@ -298,6 +298,107 @@ So if you run kitty from another kitty instance, the output will be visible
in the first kitty instance.
Adding options to kittens
----------------------------
If you would like to use kitty's config framework to make your kittens
configurable, you will need some boilerplate. Put the following files in the
directory of your kitten.
:file:`kitten_options_definition.py`
.. code-block:: python
from kitty.conf.types import Action, Definition
definition = Definition(
'!kitten_options_utils',
Action(
'map', 'parse_map',
{'key_definitions': 'kitty.conf.utils.KittensKeyMap'},
['kitty.types.ParsedShortcut', 'kitty.conf.utils.KeyAction']
),
)
agr = definition.add_group
egr = definition.end_group
opt = definition.add_option
map = definition.add_map
# main options {{{
agr('main', 'Main')
opt('some_option', '33',
option_type='some_option_parser',
long_text='''
Help text for this option
'''
)
egr() # }}}
# shortcuts {{{
agr('shortcuts', 'Keyboard shortcuts')
map('Quit', 'quit q quit')
egr() # }}}
:file:`kitten_options_utils.py`
.. code-block:: python
from kitty.conf.utils import KittensKeyDefinition, key_func, parse_kittens_key
func_with_args, args_funcs = key_func()
FuncArgsType = Tuple[str, Sequence[Any]]
def some_option_parser(val: str) -> int:
return int(val) + 3000
def parse_map(val: str) -> Iterable[KittensKeyDefinition]:
x = parse_kittens_key(val, args_funcs)
if x is not None:
yield x
Then run::
kitty +runpy 'from kitty.conf.generate import main; main()' /path/to/kitten_options_definition.py
You can parse and read the options in your kitten using the following code:
.. code-block:: python
from .kitten_options_types import Options, defaults
from kitty.conf.utils import load_config as _load_config, parse_config_base
from typing import Optional, Iterable, Dict, Any
def load_config(*paths: str, overrides: Optional[Iterable[str]] = None) -> Options:
from .kitten_options_parse import (
create_result_dict, merge_result_dicts, parse_conf_item
)
def parse_config(lines: Iterable[str]) -> Dict[str, Any]:
ans: Dict[str, Any] = create_result_dict()
parse_config_base(
lines,
parse_conf_item,
ans,
)
return ans
overrides = tuple(overrides) if overrides is not None else ()
opts_dict, found_paths = _load_config(defaults, parse_config, merge_result_dicts, *paths, overrides=overrides)
opts = Options(opts_dict)
opts.config_paths = found_paths
opts.all_config_paths = paths
opts.config_overrides = overrides
return opts
See `the code <https://github.com/kovidgoyal/kitty/tree/master/kittens/diff>`__
for the builtin :doc:`diff kitten </kittens/diff>` for examples of creating more
options and keyboard shortcuts.
Developing builtin kittens for inclusion with kitty
----------------------------------------------------------
@@ -317,8 +418,6 @@ Kittens created by kitty users
`smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
Makes the kitty scroll bindings work in full screen applications
`kitty-tab-switcher <https://github.com/OsiPog/kitty-tab-switcher>`__
Fuzzy finder for kitty tabs with previews
`gattino <https://github.com/salvozappa/gattino>`__
Integrate kitty with an LLM to convert plain language prompts into shell commands.
@@ -330,6 +429,3 @@ Kittens created by kitty users
`weechat-hints <https://github.com/GermainZ/kitty-weechat-hints>`_
URL hints kitten for WeeChat that works without having to use WeeChat's
raw-mode.
`pdf-kitten <https://github.com/gelaechter/pdf-kitten>`__
Allows you to export kitty's output to PDF, making use of weasyprint.

View File

@@ -1,136 +0,0 @@
Using terminal programs to provide Linux desktop components
===============================================================
.. only:: man
Overview
--------------
.. versionadded:: 0.43.0
Power users of terminals on Linux also often like to use bare bones window
managers instead of full fledged desktop environments. This kitten helps
provide parts of the desktop environment that are missing from such setups,
and does so using keyboard friendly, terminal first, UI components. Some of its
features are:
* Replace the typical File Open/Save dialogs used in GUI programs with the
fast and keyboard centric :doc:`choose-files </kittens/choose-files>` kitten
running in a semi-transparent kitty overlay.
* Allow simple command line based management of the desktop light/dark modes.
How to install
-------------------
.. note::
This kitten relies on the :doc:`panel kitten </kittens/panel>`
under the hood to supply UI components. Check :ref:`the documentation <panel_compat>`
of that kitten to see if your window manager works with it.
First, run::
kitten desktop-ui enable-portal
Then, set the following two environment variables, *system wide*, that means in
:file:`/etc/environment` or the equivalent for your distribution::
QT_QPA_PLATFORMTHEME=xdgdesktopportal
GTK_USE_PORTAL=1
Finally, reboot. Now, when you open a file dialog in most GUI applications, it
should open the :doc:`choose-files kitten </kittens/choose-files>` instead
of a normal file open dialog. You can change the current light/dark mode of
your desktop by running::
kitten desktop-ui set-color-scheme dark
kitten desktop-ui set-color-scheme light
Check the current value using::
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
How it works
----------------
Modern Linux desktops have so called `portals
<https://flatpak.github.io/xdg-desktop-portal/docs/index.html>`__ that were
invented for sandboxed applications and provide various facilities to such
applications over DBUS, including file open dialogs, common desktop settings,
etc. This kitten works by implementing a backend for some of these services.
Normal GUI applications can then be told to make use of these services, thereby
allowing us to replace parts of the desktop experience as needed.
There are multiple competing implementations of the backends. Each desktop
environment like KDE or GNOME has it's own backend and many window managers
provide implementations for some backends as well. Service discovery and
configuring which backend to use happens via the :file:`xdg-desktop-portal`
program, usually found at :file:`/usr/lib/xdg-desktop-portal`.
It can be configured by files in :file:`~/.local/share/xdg-desktop-portal`. See
`man portals.conf <https://man.archlinux.org/man/portals.conf.5>`__. The
``kitten desktop-ui enable-portal`` command takes care of the setup for you
automatically. If you want to customize exactly which services to use this
kitten for, run the command and then edit the conf file that the command says
it has patched.
Troubleshooting
-------------------
First, ensure that DBUS is able to auto-start the kitten when it is needed. If
the kitten is not already running, try the following command::
dbus-send --session --print-reply --dest=org.freedesktop.impl.portal.desktop.kitty \
/net/kovidgoyal/kitty/portal org.freedesktop.DBus.Properties.GetAll \
string:net.kovidgoyal.kitty.settings
If DBUS is able to start the kitten or if it is already running it will print
out the version property, otherwise it will fail with an error. If it fails,
check the file
:file:`~/.local/share/dbus-1/services/org.freedesktop.impl.portal.desktop.kitty.service`
that should have been created by the ``enable-portal`` command. It's ``Exec``
key must point to the full path to the kitten executable.
Next, check that the XDG portal system is actually using this kitten for its
settings backend. Run::
dbus-send --session --print-reply --dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop org.freedesktop.portal.Settings.Read \
string:net.kovidgoyal.kitty string:status
If this returns a reply then the kitten is being used, as expected. If it
returns a not found error, then some other backend is being used for settings.
Read the ``portals.conf`` man page and run::
/usr/lib/xdg-desktop-portal -r v
this will output a lot of debug information, which should tell you which
backend is chosen for which service. Read the debug output carefully to
determine why the kitten is not being selected.
If some GUI applications are not using the choose-files kitten for their file
select dialogs, then make sure the environment variables mentioned above are
set, you can also try running the GUI application with them set explicitly,
as::
QT_QPA_PLATFORMTHEME=xdgdesktopportal GTK_USE_PORTAL=1 my-gui-app
Note that not all applications use portals, so if some particular application
is failing to use the portal but others work, report the issue to that
applications' developers.
Configuration
------------------------
You can configure various aspects of the kitten's operation by creating a
:file:`desktop-ui-portal.conf` in your :ref:`kitty config folder <confloc>`.
See below for the supported configuration directives.
.. include:: /generated/conf-kitten-desktop_ui.rst

View File

@@ -1,53 +0,0 @@
Drag and Drop
==================================================
.. only:: man
Overview
--------------
*Drag and drop of files from the shell*
.. highlight:: sh
The ``dnd`` kitten can be used to drag and drop files between
the shell and arbitrary GUI programs, it even works over SSH, so you
can easily and seamlessly transfer files from one computer to another,
simply by dragging from one kitty window to another.
Using it is as simple as::
kitten dnd file-to-drag.xyz
Then, start dragging with the mouse inside the window, and :file:`file-to-drag.xyz`
will be dragged and you can drop it onto a GUI file manager or another window
running this kitten. You can specify directories as well to drag entire trees.
Similarly, dropping works by running the kitten::
kitten dnd
Then, drag some files from a GUI file manager or another window running the dnd kitten
and drop them onto this window. The files will be copied or moved (depending on
which area you drop them) into the current working directory.
The best part is this works even over SSH. So if you just want to quickly
transfer some files from one computer to another all you need to so is ssh into
the remote computer::
kitten ssh remote-computer-name
Then, run the dnd kitten on the remote computer::
kitten dnd files-or-dirs-to-drag
That's it, you can now drag form or drop to the remote computer. See below for
customising the behavior of the kitten via command line flags.
This kitten uses a new protocol developed by kitty to function, for details,
see :doc:`/dnd-protocol`.
.. program:: kitty +kitten dnd
.. include:: /generated/cli-kitten-dnd.rst

View File

@@ -9,7 +9,7 @@ Hyperlinked grep
.. note::
As of ripgrep versions newer than 13.0 it supports hyperlinks
As of ripgrep versions newer that 13.0 it supports hyperlinks
natively so you can just add the following alias in your shell rc file:
``alias rg="rg --hyperlink-format=kitty"`` no need to use this kitten.
But, see below for instructions on how to customize kitty to have it open
@@ -45,7 +45,7 @@ Now, run a search with::
Hold down the :kbd:`Ctrl+Shift` keys and click on any of the result lines, to
open the file in :program:`vim` at the matching line. If you use some editor
other than :program:`vim`, you should adjust the :file:`open-actions.conf` file
accordingly. To open links with the keyboard instead, use
accordingly. TO open links with the keyboard instead, use
:sc:`open_selected_hyperlink`.
Finally, add an alias to your shell's rc files to invoke the kitten as
@@ -67,7 +67,7 @@ which items are linked with a :code:`--kitten hyperlink` flag. For example,
:code:`--kitten hyperlink=matching_lines` will only add hyperlinks to the
match lines. :code:`--kitten hyperlink=file_headers,context_lines` will link
file headers and context lines but not match lines. :code:`--kitten
hyperlink=none` will cause the command line to be passed directly to
hyperlink=none` will cause the command line to be passed to directly to
:command:`rg` so no hyperlinking will be performed. :code:`--kitten hyperlink`
may be specified multiple times.

View File

@@ -114,12 +114,11 @@ shell.
The Linux dock panel was::
wm bar
kitten panel kitty +launch my-panel.py
This is a custom program I wrote for my personal use. It uses kitty's kitten
infrastructure to implement the bar in a `few hundred lines of code
<https://github.com/kovidgoyal/wm/blob/master/bar/main.go>`__.
This was designed for my personal use only, but, there are :ref:`public projects implementing
This creates the panel window and runs the ``my-panel.py`` script inside it
using the Python interpreter that comes bundled with kitty. Unfortunately the
actual script is not public, but there are :ref:`public projects implementing
general purpose panels using kitty <panel_projects>`.
@@ -139,7 +138,7 @@ Compatibility with various platforms
.. tab:: Wayland
Below is a list of the status of various Wayland compositors. The panel kitten
relies on the `wlr layer shell protocol
relies of the `wlr layer shell protocol
<https://wayland.app/protocols/wlr-layer-shell-unstable-v1#compositor-support>`__,
which is technically supported by almost all Wayland compositors, but the
implementation in some of them is quite buggy.
@@ -147,24 +146,29 @@ Compatibility with various platforms
🟢 **Hyprland**
Fully working, no known issues
🟢 **labwc**
🟢 **KDE** (kwin)
Fully working, no known issues
🟢 **niri**
Fully working, no known issues
🟢 **river**
Fully working, no known issues
🟢 **Xfce**
Fully working, no known issues
🟠 **KDE** (kwin)
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.
🟠 **Sway**
Renders its configured background over the background window instead of
under it. This is because it uses the wlr protocol for backgrounds itself.
Partially working. Issues include:
* Renders its configured background over the background window instead of
under it. This is likely because it uses the wlr protocol for
backgrounds itself.
* Hiding a dock panel (unmapping the window) does not release the space
used by the dock.
🟠 **niri**
Breaks when hiding (unmapping) layer shell windows. This means the quick
access terminal is non-functional, but background and dock panels work.
More technically, keyboard focus gets stuck in the hidden window and when trying
to remap the hidden window niri never sends configure events for the remapped surface.
🟠 **labwc**
Breaks when hiding (unmapping) layer shell windows. This means the quick
access terminal is non-functional, but background and dock panels work.
More technically, when unmapping the surface (attaching a NULL buffer to
it) labwc continues to send configure events to the unmapped surface,
leading to Wayland protocol errors and a crash of labwc.
🔴 **GNOME** (mutter)
Does not implement the wlr protocol at all, nothing works.

View File

@@ -13,10 +13,7 @@ Extend with kittens
kittens/themes
kittens/choose-fonts
kittens/hints
kittens/command-palette
kittens/quick-access-terminal
kittens/choose-files
kittens/dnd
kittens/panel
kittens/remote_file
kittens/hyperlinked_grep
@@ -56,11 +53,6 @@ Some prominent kittens:
filenames, words, lines, etc. from the terminal screen.
:doc:`Command palette <kittens/command-palette>`
Browse, search and trigger all keyboard shortcuts and actions from a
single searchable overlay.
:doc:`Quick access terminal <kittens/quick-access-terminal>`
Get access to a quick access floating, semi-transparent kitty window
with a single keypress.
@@ -71,14 +63,6 @@ Some prominent kittens:
terminal programs.
:doc:`Choose files <kittens/choose-files>`
Preview and select files at the speed of thought
:doc:`Drag and drop from the shell <kittens/dnd>`
Drag and drop files from/to GUI programs from your shell, even over SSH
:doc:`Remote file <kittens/remote_file>`
Edit, open, or download remote files over SSH easily, by simply clicking on
the filename.
@@ -107,6 +91,11 @@ Some prominent kittens:
configuration to the remote host.
:doc:`Panel <kittens/panel>`
Draw a GPU accelerated dock panel on your desktop showing the output from an
arbitrary terminal program.
:doc:`Clipboard <kittens/clipboard>`
Copy/paste to the clipboard from shell scripts, even over SSH.

View File

@@ -130,7 +130,7 @@ create :file:`~/.config/kitty/mywatcher.py` and use :option:`launch --watcher` =
def on_load(boss: Boss, data: dict[str, Any]) -> None:
# This is a special function that is called just once when this watcher
# module is first loaded, can be used to perform any initialization/one
# module is first loaded, can be used to perform any initializztion/one
# time setup. Any exceptions in this function are printed to kitty's
# STDERR but otherwise ignored.
...
@@ -175,25 +175,6 @@ create :file:`~/.config/kitty/mywatcher.py` and use :option:`launch --watcher` =
# code received from the program running in the window
...
def on_tab_bar_dirty(boss: Boss, window: Window, data: dict[str, Any]) -> None:
# called when any changes happen to the tab bar, such a new tabs being
# created, tab titles changing, tabs moving, etc. Useful to display the
# tab bar externally to kitty. This is called even if the tab bar is
# hidden. Note that this is called only in *global watchers*, that is
# watchers defined in kitty.conf or using the --watcher command line
# flag. data contains tab_manager which is the object responsible for
# managing all tabs in a single OS Window.
...
def on_quit(boss: Boss, window: Window, data: dict[str, Any]) -> None:
# called when kitty is about to quit. This is called in *global watchers*
# only. It is called twice: once before the quit confirmation dialog is
# shown (data['confirmed'] will be False) and once after the user has
# confirmed quitting (data['confirmed'] will be True). Setting
# data['aborted'] to True will abort the quit in both cases.
...
Every callback is passed a reference to the global ``Boss`` object as well as
the ``Window`` object the action is occurring on. The ``data`` object is a dict
that contains event dependent data. You have full access to kitty internals in

View File

@@ -184,36 +184,12 @@ define a few extra key bindings in :file:`kitty.conf`::
# window's size.
map ctrl+. layout_action bias 80
# Maximize the active window along the horizontal axis (fill full width),
# keeping other windows visible in their vertical positions. Press again to
# restore the original layout.
map ctrl+shift+right layout_action maximize horizontal
# Maximize the active window along the vertical axis (fill full height),
# keeping other windows visible in their horizontal positions. Press again
# to restore the original layout.
map ctrl+shift+up layout_action maximize vertical
# Equalize all splits so that windows share available space proportionally.
map ctrl+shift+e layout_action equalize
Windows can be resized using :ref:`window_resizing`. You can swap the windows
in a split using the ``rotate`` action with an argument of ``180`` and rotate
and swap with an argument of ``270``. The ``maximize`` action expands the active
window to fill the maximum available space along a single axis while keeping
the rest of the layout intact. Use ``maximize horizontal`` to fill the full
width and ``maximize vertical`` to fill the full height. Calling it again
restores the original split sizes. The ``equalize`` action redistributes space
so that all windows along each split axis receive an equal share.
and swap with an argument of ``270``.
This layout takes two options. ``equalize_on_close`` automatically equalizes
split sizes whenever a window is closed, keeping remaining windows balanced
without needing an explicit keybinding::
enabled_layouts splits:equalize_on_close=true
``split_axis`` controls whether new windows
This layout takes one option, ``split_axis`` that controls whether new windows
are placed into vertical or horizontal splits when a :option:`--location
<launch --location>` is not specified. A value of ``horizontal`` (same as
``--location=vsplit``) means when a new split is created the two windows will
@@ -289,14 +265,7 @@ All windows are shown one below the other. This layout has no options::
Resizing windows
------------------
You can resize windows inside layouts. The easiest method is to simply drag the
window borders using a mouse, controlled by the option :opt:`window_drag_tolerance`.
Note that technically this resizes layout slots not actual windows, so it
does not work exactly like resizing OS Windows on your desktop. Instead, the
layout is changed and potentially multiple windows get resized when dragging a
single border.
For keyboard friendly resizing, press :sc:`start_resizing_window` (also
You can resize windows inside layouts. Press :sc:`start_resizing_window` (also
:kbd:`⌘+r` on macOS) to enter resizing mode and follow the on-screen
instructions. In a given window layout only some operations may be possible for
a particular window. For example, in the *Tall* layout you can make the first

View File

@@ -167,33 +167,6 @@ the program running in the terminal, map it to :ac:`discard_event`::
.. _conditional_mappings:
Configuring a timeout
----------------------
You can also set a timeout for keyboard modes and multi-key mappings. If a
timeout is set and you don't complete the key sequence or exit the mode within
the specified time, the mode will be automatically cancelled. This is useful
for multi-key mappings where you might accidentally press the first key and
then change your mind. The timeout is specified in seconds and can be set
globally using the :opt:`map_timeout` option or per-mode using ``--timeout``::
# Set a global 2 second timeout for all multi-key and modal mappings
map_timeout 2.0
# This mode will have a 5 second timeout (overrides global setting)
map --new-mode resize --timeout 5.0 kitty_mod+r
map --mode resize h resize_window narrower
map --mode resize l resize_window wider
# ... more mappings
# Multi-key mapping with the global timeout
map ctrl+a>h new_window
When a timeout occurs, the mode is exited and any buffered keys are discarded.
A timeout value of zero disables the timeout. For multi-key sequences, the
timeout is restarted after each valid key press in the sequence.
Conditional mappings depending on the state of the focused window
----------------------------------------------------------------------
@@ -221,14 +194,14 @@ running an editor::
map --when-focus-on var:in_editor kitty_mod+c
In order to make this work, you need to configure your editor as shown below:
In order to make this work, you need to configure your editor as show below:
.. tab:: vim
In :file:`~/.vimrc` add:
.. code-block:: vim
let &t_ti = &t_ti . "\033]1337;SetUserVar=in_editor=MQ==\007"
let &t_ti = &t_ti . "\033]1337;SetUserVar=in_editor=MQo\007"
let &t_te = &t_te . "\033]1337;SetUserVar=in_editor\007"
.. tab:: neovim
@@ -237,25 +210,17 @@ In order to make this work, you need to configure your editor as shown below:
.. code-block:: lua
vim.api.nvim_create_autocmd({ "VimEnter", "VimResume", "UIEnter" }, {
vim.api.nvim_create_autocmd({ "VimEnter", "VimResume" }, {
group = vim.api.nvim_create_augroup("KittySetVarVimEnter", { clear = true }),
callback = function()
if vim.api.nvim_ui_send then
vim.api.nvim_ui_send("\x1b]1337;SetUserVar=in_editor=MQ==\007")
else
io.stdout:write("\x1b]1337;SetUserVar=in_editor=MQ==\007")
end
io.stdout:write("\x1b]1337;SetUserVar=in_editor=MQo\007")
end,
})
vim.api.nvim_create_autocmd({ "VimLeave", "VimSuspend" }, {
group = vim.api.nvim_create_augroup("KittyUnsetVarVimLeave", { clear = true }),
callback = function()
if vim.api.nvim_ui_send then
vim.api.nvim_ui_send("\x1b]1337;SetUserVar=in_editor\007")
else
io.stdout:write("\x1b]1337;SetUserVar=in_editor\007")
end
io.stdout:write("\x1b]1337;SetUserVar=in_editor\007")
end,
})
@@ -265,67 +230,6 @@ copying to clipboard. In the editor, you can map it to copy to the clipboard,
thereby allowing use of a common shortcut both inside and outside the editor
for copying to clipboard.
.. note::
When using multi-key mappings, of the form :kbd:`k1>k2` or similar, the
condition applies to the first key and you can have only one condition per
key, the last in kitty.conf wins. In particular, this means you cannot have
multiple conditions applying to multi-key mappings with the same first key
and you cannot have mappings with and without conditions applying to multi-keys
with the same first key.
.. _mapping-fallback:
Non-Latin keyboard layout support
--------------------------------------
When using a non-Latin keyboard layout (e.g. Russian, Arabic, Greek), letter-key
shortcuts like :kbd:`Ctrl+C` stop working because the key produces a non-Latin
character. kitty solves this with the ``--allow-fallback`` option on the ``map``
directive, which controls how shortcuts fall back to the physical key position.
The ``--allow-fallback`` option accepts a comma-separated list of fallback types:
``shifted``
Fall back to the *shifted key* — the character produced when Shift is held
with the key. This is the default for all mappings and preserves the existing
behavior.
``ascii``
Fall back to the *alternate key* — the character that the physical key would
produce in a standard US layout. This only triggers when the key produces a
non-ASCII character, so it has no effect on Latin-based layouts like Dvorak
or Colemak.
``none``
Disable all fallback matching. The mapping will only match the exact key
specified, ignoring both shifted and alternate key positions.
For example::
# Enable both shifted and ASCII fallback (used by default kitty shortcuts)
map --allow-fallback=shifted,ascii kitty_mod+c copy_to_clipboard
# Only ASCII fallback, no shifted key fallback
map --allow-fallback=ascii ctrl+s save_something
# Disable all fallback (neither shifted nor alternate key matching)
map --allow-fallback=none ctrl+x some_action
All default kitty shortcuts use ``--allow-fallback=shifted,ascii``, so they work
out of the box with non-Latin layouts. Custom mappings without an explicit
``--allow-fallback`` get the default value of ``shifted``, which preserves
backward compatibility.
.. note::
The ``ascii`` fallback uses a non-ASCII guard: it only activates when
the key produces a character with a Unicode code point above 127. This means
alternative Latin layouts (Dvorak, Colemak, etc.) are never affected by the
``ascii`` fallback — only non-Latin layouts trigger it.
Sending arbitrary text or keys to the program running in kitty
--------------------------------------------------------------------------------
@@ -406,7 +310,7 @@ below::
# Beep on unknown keys
map --new-mode XXX --on-unknown beep ...
# Ignore unknown keys silently
# Ingore unknown keys silently
map --new-mode XXX --on-unknown ignore ...
# Beep and exit the keyboard mode on unknown key
map --new-mode XXX --on-unknown end ...

View File

@@ -29,28 +29,6 @@ there is only one number to reset these attributes, SGR 22, which resets both.
There is no way to reset one and not the other. kitty uses 221 and 222 to reset
bold and faint independently.
.. _mouse_leave_window:
Reporting when the mouse leaves the window
----------------------------------------------
kitty extends the SGR Pixel mouse reporting protocol created by xterm to
also report when the mouse leaves the window. This event is delivered
encoded as a normal SGR pixel event except that the eight bit is set on the
first number. Additionally, bit 5 is set to indicate this is a motion related event.
The remaining bits 1-7 (except 5) are used to encode button and modifier information.
When bit 8 is set it means the event is a mouse has left the window event,
and all other bits should be ignored. The pixel position values must also
be ignored as they may not be accurate.
An escape code to move the contents of the screen into the scrollback
-------------------------------------------------------------------------------------
The escape code is ``\x1b [ 22 J`` (ignoring spaces present for clarity). It
moves all screen contents (text and images) into the scrollback leaving the
screen in the same state as it would be if the standard screen clear escape
code had been used ``\x1b [ 2 J``.
kitty specific private escape codes
---------------------------------------

View File

@@ -1,247 +0,0 @@
The multiple cursors protocol
==============================================
.. versionadded:: 0.43.0
Many editors support something called *multiple cursors* in which you can make
the same changes at multiple locations in a file and the editor shows you
cursors at each of the locations. In a terminal context editors typically
implement this by showing some Unicode glyph at each location instead of the
actual cursor. This is sub-optimal since actual cursors implemented by the
terminal have many niceties like smooth animation [anim]_, auto adjust colors [rv]_,
etc. To address this and other use cases, this protocol allows terminal programs to
request that the terminal display multiple cursors at specific locations on the
screen.
Quickstart
----------------
An example, showing how to use the protocol:
.. code-block:: sh
# Show cursors of the same shape as the main cursor at y=4, x=5
printf "\e[>29;2:4:5 q"
# Show more cursors on the seventh line, of various shapes, the underline shape is shown twice
printf "\e[>1;2:7:1 q\e[>2;2:7:3 q\e[>3;2:7:5;2:7:7 q"
The escape code to show a cursor has the following structure (ignore spaces
they are present for readability only)::
CSI > SHAPE;CO-ORD TYPE : CO-ORDINATES ; CO-ORD TYPE : CO-ORDINATES ... TRAILER
Here ``CSI`` is the two bytes ESC (``0x1b``) and [ (``0x5b``). ``SHAPE`` can be
one of:
* ``0``: No cursor
* ``1``: Block cursor
* ``2``: Beam cursor
* ``3``: Underline cursor
* ``29``: Follow the shape of the main cursor
* ``30``: Change the color of text under extra cursors
* ``40``: Change the color of extra cursors
* ``100``: Used for querying currently set cursors
``CO-ORD TYPE`` can be one of:
* ``0``: This refers to the position of the main cursor and has no following
co-ordinates.
* ``2``: In this case the following co-ordinates are pairs of numbers pointing
to cells in the form ``y:x`` with the origin in the top left corner at
``1,1``. There can be any number of pairs, the terminal must treat each pair
as a new location to set a cursor.
* ``4``: In this case the following co-ordinates are sets of four numbers that
define a rectangle in the same co-ordinate system as above of the form:
``top:left:bottom:right``. The shape is set on every cell in the rectangle
from the top left cell to the bottom right cell, inclusive. If no numbers
are provided, the rectangle is the full screen. There can be any number of
rectangles, the terminal must treat each set of four numbers as a new
rectangle.
The sequence of ``CO-ORD TYPE : CO-ORDINATES`` can be repeated any number of
times separated by ``;``. The ``SHAPE`` will be set on the cells indicated by
each such group. For example: ``-1;2:3:4;4:5:6:7:8`` will set the shape ``-1``
at the cell ``(3, 2)`` and in the rectangle ``(6, 5)`` to ``(8, 7)`` inclusive.
Finally, the ``TRAILER`` terminates the sequence and is the bytes SPACE
(``0x20``) and q (``0x71``).
Terminals **must** ignore cells that fall outside the screen. That means, for
rectangle co-ordinates only the intersection of the rectangle with the screen
must be considered, and point co-ordinates that fall outside of the screen are
simply ignored, with no effect.
Terminals **must** ignore extra co-ordinates, that means if an odd number of
co-ordinates are specified for type ``2`` the last co-ordinate is ignored.
Similarly for type ``4`` if the number of co-ordinates is not a multiple of
four, the last ``1 <= n <= 3`` co-ordinates are ignored, as if they were not
specified.
Querying for support
-------------------------
A terminal program can query the terminal emulator for support of this
protocol by sending the escape code::
CSI > TRAILER
In this case a supporting terminal must reply with::
CSI > 1;2;3;29;30;40;100;101 TRAILER
Here, the list of numbers indicates the cursor shapes and other operations
the terminal supports and can be any subset of the above. No numbers
indicates the protocol is not supported. To avoid having to wait with a
timeout for a response from the terminal, the client should send this
query code immediately followed by a request for the
`primary device attributes <https://vt100.net/docs/vt510-rm/DA1.html>`_.
If the terminal responds with an answer for the device attributes without
an answer for the *query* the terminal emulator does not support this protocol at all.
Terminals **must** respond to these queries in FIFO order, so that
multiplexers that split a single screen know which split to send responses too.
Clearing previously set multi-cursors
------------------------------------------
The cursor at a cell is cleared by setting its shape to ``0``.
The most common operation is to clear all previously set multi-cursors. This is
easily done using the *rectangle* co-ordinate system above, like this::
CSI > 0;4 TRAILER
For more precise control different co-ordinate types can be used. This is
particularly important for multiplexers that split up the screen and therefore
need to re-write these escape codes.
.. _extra_cursor_color:
Changing the color of extra cursors
---------------------------------------
In order to visually distinguish extra cursors from the main cursor, it is
possible to specify a color pair for extra cursors. Note that for performance
reasons, there is only a single color pair that all extra cursors share.
The color pair consists of the cursor color and the color for text in the cell
the cursor is on.
To change this color pair use an escape code of the form::
CSI > WHICH ; COLOR_SPACE : COLOR_PARAMETER1 : COLOR_PARAMETER2 : ... TRAILER
Here, ``WHICH`` is ``30`` to set the color of text under the cursor and ``40``
to set the color of the cursor itself (these numbers mimic the SGR codes for
foreground and background respectively).
The ``COLOR_SPACE`` parameter sets the type of color, it can take values:
``0`` - unset color is same as for main cursor. No color parameters.
``1`` - *special* which typically means some kind of reverse video effect, see below
``2`` - sRGB color, with three color parameters, red, green and blue as numbers
from 0 to 255
``5`` - Indexed color with one color parameter which is an index into the color
table from 0 to 255
When the cursor color is set to *special* via ``40`` it means the block cursor
must be rendered with a reverse video effect where the cursor color becomes the
foreground color of the cell under the cursor and the foreground color of the
cell becomes its background color. Implementations are free to adjust these
colors to ensure suitable contrast levels. In this case the text color set by
``30`` must be ignored.
When the cursor color is not set to *special* but the text color via ``30`` is
set to special, then that means the foreground color of the cell with the
cursor must be changed to its background color for a partial reverse video
effect.
When unset, aka, set to ``0`` the cursors must be the same color as the main
cursor. In particular if the main color is using a reverse video effect, the
extra cursors must use the exact same colors as the main cursor, not the colors
of the cells they are on.
Querying for already set cursors
--------------------------------------
Programs can ask the terminal what extra cursors are currently set, by sending
the escape code::
CSI > 100 TRAILER
The terminal must respond with **one** escape code::
CSI > 100; SHAPE:CO-ORDINATE TYPE:CO-ORDINATES ; ... TRAILER
Here, the ``SHAPE:CO-ORDINATE TYPE:CO-ORDINATES`` block can be repeated any
number of times, separated by ``;``. This response gives the set of shapes and
positions currently active. If no cursors are currently active, there will be
no blocks, just an empty response of the form::
CSI > 100 TRAILER
Again, terminals **must** respond in FIFO order so that multiplexers know where
to direct the responses.
Querying for extra cursor colors
-------------------------------------
Programs can ask the terminal what cursor colors are currently set, by sending
escape code::
CSI > 101 TRAILER
The terminal must respond with **one** escape code::
CSI > 101 ; 30 : COLOR_SPACE : COLOR_PARAMETERS ; 40 : COLOR_SPACE : COLOR_PARAMETERS TRAILER
The number and type of ``COLOR_PARAMETERS`` depends on the preceding
``COLOR_SPACE`` and can be omitted for some ``COLOR_SPACE`` values. See the
section :ref:`extra_cursor_color` for details.
Interaction with other terminal controls and state
-------------------------------------------------------
**The main cursor**
The extra cursors must all have the same color and opacity and blink state
as the main cursor. The main cursor's visibility must not affect the
visibility of the extra cursors. Their visibility and shape are controlled
only by this protocol.
**Clearing the screen**
The escape codes used to clear the screen (`ED <https://vt100.net/docs/vt510-rm/ED.html>`__)
with parameters 2, 3 and 22 must remove all extra cursors,
this is so that the clear command can be used by users to clear the screen of extra cursors.
**Reset***
This must remove all extra cursors.
**Alternate screen***
Switching between the main and alternate screens must remove all extra
cursors.
**Scrolling**
The index (IND) and reverse index (RI) escape codes that cause screen
contents to scroll into scrollback or off screen must not affect
the extra cursors in any way. They remain at exactly the same position.
It is up to applications to manage extra cursor positions when using these
escape codes if needed. There are not a lot of use cases for scrolling
extra cursors with screen content, since extra cursors are meant to be
ephemeral and on screen only, not in scrollback. This allows terminals
to avoid the extra overhead of adjusting positions of the extra cursors
on every scroll.
Footnotes
-------------
.. [anim] kitty allows the cursor blink to be :opt:`animated
<cursor_blink_interval>` using any CSS easing function. This cannot be
implemented using fake cursors.
.. [rv] kitty has a special "reverse video" color mode for cursors where the
color of the cursor and the text under the cursor is adjusted based on the
color of the cell under the cursor. This also cannot be implemented using
fake cursors.

View File

@@ -48,7 +48,7 @@ action per entry if you like, for example:
action change_font_size current -2
In the launch specification you can expand environment variables, as shown in
In the action specification you can expand environment variables, as shown in
the examples above. In addition to regular environment variables, there are
some special variables, documented below:
@@ -126,12 +126,11 @@ lines. The various available criteria are:
.. _launch_actions:
Scripting the opening of files with kitty
Scripting the opening of files with kitty on macOS
-------------------------------------------------------
On macOS you can use :guilabel:`Open With` in Finder or drag and drop files and
URLs onto the kitty dock icon to open them with kitty. Similarly on Linux, you
can associate certain files types to open in kitty. The default actions are:
URLs onto the kitty dock icon to open them with kitty. The default actions are:
* Open text files in your editor and images using the icat kitten.
* Run shell scripts in a shell

View File

@@ -33,9 +33,6 @@ frames-per-second. Press :sc:`edit_config_file` in kitty to open its fully
commented sample config file in your text editor. For details see the
:doc:`configuration docs <conf>`.
You can quickly browse all available mappable actions by pressing
:sc:`command_palette`, see :doc:`kittens/command-palette` for details.
.. toctree::
:hidden:
@@ -99,16 +96,12 @@ used to create programs that leverage kitty's powerful features, for example,
You can :doc:`create your own kittens to scratch your own itches
<kittens/custom>`.
For a list of all the builtin kittens, run ``kitten`` in kitty, or to browse
some of the more prominent ones, see :ref:`see here <kittens>`.
For a list of all the builtin kittens, :ref:`see here <kittens>`.
Additionally, you can use the :ref:`watchers <Watchers>` framework
to create Python scripts that run in response to various events such as windows
being resized, closing, having their titles changed, etc.
Finally, there is remote control which allows you to control kitty from
anywhere, even across a network! See below for more about remote control.
.. toctree::
:hidden:
@@ -131,14 +124,88 @@ started.
remote-control
Sessions
.. _sessions:
Startup Sessions
------------------
You can control the :term:`tabs <tab>`, :term:`kitty window <window>` layout,
working directory, startup programs, etc. by creating a *session* file and using
the :option:`kitty --session` command line flag or the :opt:`startup_session`
option in :file:`kitty.conf`. You can also easily switch between sessions with
a keypress. See :doc:`sessions` for details.
option in :file:`kitty.conf`. An example, showing all available commands:
.. code-block:: session
# Set the layout for the current tab
layout tall
# Set the working directory for windows in the current tab
cd ~
# Create a window and run the specified command in it
launch zsh
# Create a window with some environment variables set and run vim in it
launch --env FOO=BAR vim
# Set the title for the next window
launch --title "Chat with x" irssi --profile x
# Run a short lived command and see its output
launch --hold message-of-the-day
# Create a new tab
# The part after new_tab is the optional tab title which will be displayed in
# the tab bar, if omitted, the title of the active window will be used instead.
new_tab my tab
cd ~/somewhere
# Set the layouts allowed in this tab
enabled_layouts tall,stack
# Set the current layout
layout stack
launch zsh
# Create a new OS window
# Any definitions specified before the first new_os_window will apply to first OS window.
new_os_window
# Set new window size to 80x24 cells
os_window_size 80c 24c
# Set the --class for the new OS window
os_window_class mywindow
# Set the --name for the new OS window
os_window_name myname
# Change the OS window state to normal, fullscreen, maximized or minimized
os_window_state normal
launch sh
# Resize the current window (see the resize_window action for details)
resize_window wider 2
# Make the current window the active (focused) window in its tab
focus
# Make the current OS Window the globally active window (not supported on Wayland)
focus_os_window
launch emacs
# Create a complex layout using multiple splits. Creates two columns of
# windows with two windows in each column. The windows in the first column are
# split 50:50. In the second column the windows are not evenly split.
new_tab complex tab
layout splits
# First window, set a user variable on it so we can focus it later
launch --var window=first
# Create the second column by splitting the first window vertically
launch --location=vsplit
# Create the third window in the second column by splitting the second window horizontally
# Make it take 40% of the height instead of 50%
launch --location=hsplit --bias=40
# Go back to focusing the first window, so that we can split it
focus_matching_window var:window=first
# Create the final window in the first column
launch --location=hsplit
.. note::
The :doc:`launch <launch>` command when used in a session file cannot create
new OS windows, or tabs.
.. note::
Environment variables of the form :code:`${NAME}` or :code:`$NAME` are
expanded in the session file, except in the *arguments* (not options) to the
launch command.
Creating tabs/windows
@@ -181,13 +248,6 @@ You can also customize what happens when clicking on :term:`hyperlinks` in
kitty, having it open files in your editor, download remote files, open things
in your browser, etc. For details, see :doc:`here <open_actions>`.
Additionally, various bits of the kitty UI itself work with the mouse. You can
drag and drop tabs in the tab bar to re-order them or move them from one OS
Window to another, or even pop them out into a new OS Window.
You can drag window borders to resize windows. You can double click on empty regions
of the tab bar to create new tabs or double click on an existing tab to rename
it.
.. toctree::
:hidden:
@@ -211,10 +271,9 @@ The scrollback buffer
-----------------------
|kitty| supports scrolling back to view history, just like most terminals. You
can use either keyboard shortcuts or the mouse scroll wheel to do so. |kitty|
displays an interactive :opt:`scrollbar` along the right edge
of the window that shows your current position in the scrollback. You can click
and drag the scrollbar to quickly navigate through the history.
can use either keyboard shortcuts or the mouse scroll wheel to do so. While
you are browsing the scrollback a :opt:`small indicator <scrollback_indicator_opacity>`
is displayed along the right edge of the window to show how far back you are.
However, |kitty| has an extra, neat feature. Sometimes you need to explore the scrollback
buffer in more detail, maybe search for some text or refer to it side-by-side
@@ -233,7 +292,7 @@ arbitrary, command running in a new :term:`window`, :term:`tab` or
Would open the scrollback buffer in a new :term:`window` when you press the
:kbd:`F1` key. See :sc:`show_scrollback <show_scrollback>` for details.
If you want to use it with an editor such as :program:`nvim` to get more powerful
If you want to use it with an editor such as :program:`vim` to get more powerful
features, see for example, `kitty-scrollback.nvim
<https://github.com/mikesmithgh/kitty-scrollback.nvim>`__ or `kitty-grab <https://github.com/yurikhan/kitty_grab>`__
or see more tips for using various editor programs, in :iss:`this thread <719>`.

View File

@@ -101,7 +101,7 @@ ASCII only text.
foot, iterm2 and Terminal.app are left out as they do not run under X11.
Alacritty+tmux is included just to show the effect of putting a terminal
multiplexer into the mix (halving throughput) and because alacritty isn't
multiplexer into the mix (halving throughput) and because alacritty isnt
remotely comparable to any of the other terminals feature wise without tmux.
.. note::

View File

@@ -28,8 +28,6 @@ please do so by opening issues in the `GitHub bug tracker
graphics-protocol
keyboard-protocol
text-sizing-protocol
dnd-protocol
multiple-cursors-protocol
file-transfer-protocol
desktop-notifications
pointer-shapes

View File

@@ -1,7 +1,6 @@
.. only:: not man
.. sidebar::
.. only:: not man
.. sidebar::
**Screenshots**

View File

@@ -186,7 +186,7 @@ Now, using this password, you can, in scripts run the command::
Any script with access to the password can now change colors in kitty using
remote control, but only that and nothing else. You can even supply the
password via the :envvar:`KITTY_RC_PASSWORD` environment variable, or the
file :file:`~/.config/kitty/rc-pass` to avoid having to type it repeatedly.
file :file:`~/.config/kitty/rc-password` to avoid having to type it repeatedly.
See :option:`kitten @ --password-file` and :option:`kitten @ --password-env`.
The :opt:`remote_control_password` can be specified multiple times to create
@@ -262,12 +262,6 @@ as shown below:
return True
.. note::
The payloads for the different remote control commands are documented in the
:doc:`remote control protocol specification <rc_protocol>`.
.. _rc_mapping:
Mapping key presses to remote control commands
@@ -298,8 +292,7 @@ control commands as you like and process their output.
:ac:`launch` command with ``--type=background --allow-remote-control``.
For more advanced usage, including fine grained permissions, setting
env vars, command line interpolation, passing data to STDIN, etc.
the :doc:`launch <launch>` command should be used. Relative paths to scripts
are interpreted with respect to the kitty config directory.
the :doc:`launch <launch>` command should be used.
.. note:: You do not need :opt:`allow_remote_control` to use these mappings,
as they are not actual remote programs, but are simply a way to reuse the

View File

@@ -1,8 +1,6 @@
# sphinx-inline-tabs breaks with sphinx >= 9
sphinx <= 8.2.3
sphinx
furo
sphinx-copybutton
sphinxext-opengraph
sphinx-inline-tabs
sphinx-autobuild
matplotlib

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 KiB

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

After

Width:  |  Height:  |  Size: 2.7 MiB

View File

@@ -1,377 +0,0 @@
.. _sessions:
Sessions
=============
kitty has robust support for sessions. A session is basically a simple text
file where you can define kitty windows, tabs and what programs to run in them
as well as how to layout the windows. kitty also supports actions to easily
:ac:`create and switch between existing sessions <goto_session>`, so that you
can move seamlessly from working on one project to another with a couple of keystrokes.
Let's see a quick example to get a feel of how easy it is to create sessions. First,
a session file to develop a project:
.. code-block:: session
# Set the layout for the current tab
layout tall
# Set the working directory for windows in the current tab
cd ~/path/to/myproject
# Create the "main" window and run an editor in it to edit the project files
launch --title "Edit My Project" /usr/bin/nvim
# Create a side window to run a shell to build or test project
launch --title "Build My Project"
# Create another side window to keep an eye on some useful log file
launch --title "Log for my project" /usr/bin/tail -f /path/to/project/log/file
Save this file as :file:`~/path/to/myproject/launch.kitty-session`. Now when
you want to work on the project, simply run:
.. code-block:: sh
kitty --session ~/path/to/myproject/launch.kitty-session
You can also set the session in :file:`kitty.conf` via :opt:`startup_session`.
Thus, it is very easy to create sessions and work on projects. To learn how to
create more complex sessions, see :ref:`complex_sessions`.
.. _goto_session:
Creating/Switching to sessions with a keypress
------------------------------------------------
If you like to manage multiple sessions within a single terminal and
easily swap between them, kitty has you covered. You can use the
:ac:`goto_session` action in kitty.conf, like this:
.. code-block:: conf
# Press F7 and then c to jump to the "cool" project
map f7>c goto_session ~/path/to/cool/cool.kitty-session
# Press F7 and then h to jump to the "hot" project
map f7>h goto_session ~/path/to/hot/hot.kitty-session
# Browse and select from the list of known projects defined via goto_session commands
map f7>/ goto_session
# Browse and select from the list of active projects defined via goto_session commands
map f7>/ goto_session --active-only [=no]
# Same as above, but the sessions are listed alphabetically instead of by most recent
map f7>/ goto_session --sort-by=alphabetical
# Browse session files inside a directory and pick one
map f7>p goto_session ~/.local/share/kitty/sessions
# Go to the previously active session (larger negative numbers jump further back in history)
map f7>- goto_session -1
In this manner you can define as many projects/sessions as you like and easily
switch between them with a keypress.
When a directory path is supplied to :ac:`goto_session`, kitty scans it for
files ending in ``.kitty-session``, ``.kitty_session`` or ``.session`` and
presents an interactive list. The ``--sort-by`` option controls the ordering of that list just like it does
for globally known sessions.
You can also close sessions using the :ac:`close_session` action, which closes
all windows in the session with a single keypress.
Displaying the currently active session name
----------------------------------------------
You can display the name of the currently active session file in the kitty tab
bar using :opt:`tab_title_template`. For example, using the value::
{session_name} {title}
will show you the name of the session file the current tab was loaded from, as
well as the normal tab title. Or alternatively, you can set the tab title
directly to a project name in the session file itself when creating the tab,
like this::
new_tab My Project Name
.. _complex_sessions:
More complex sessions
-------------------------
If you want to create more complex sessions, with sophisticated layouts, such
as :ref:`splits_layout`, the easiest way is to set up the state you want to
save manually by first starting kitty like this:
.. code-block:: sh
kitty -o 'map f1 save_as_session --use-foreground-process --relocatable'
Now create whatever splits and tabs you need and start whatever programs such
as editors, REPLs, debuggers, etc. you want to start in each of them. Once
kitty is the way you want it, press the :kbd:`F1` key, and you will be prompted
for a path at which to save the session file. Specify the path and the session
will be saved there with the exact setup you created. The saved file will even
be opened in your editor for you to review, automatically.
.. tip::
If you want session files to be saved to a specific directory regardless of
your current working directory, use the ``--base-dir`` option. For example::
map f7>s save_as_session --use-foreground-process --base-dir ~/.local/share/kitty/sessions
This is particularly useful when kitty is launched from system-wide shortcuts
where the working directory might not be your home directory. Note that
``--relocatable`` is typically not used with ``--base-dir``, since relocatable
is meant for session files that are co-located with their project directories.
If instead, you want to create these by hand, see the example below which shows
all the major keywords you can use in kitty session files:
.. code-block:: session
# Set the layout for the current tab
layout tall
# Set the working directory for windows in the current tab. Relative paths
# are resolved with respect to the location of this session file.
cd ~
# Create a window and run the specified command in it
launch zsh
# Create a window with some environment variables set and run vim in it
launch --env FOO=BAR vim
# Set the title for the next window
launch --title "Chat with x" irssi --profile x
# Run a short lived command and see its output
launch --hold message-of-the-day
# Create a new tab
# The part after new_tab is the optional tab title which will be displayed in
# the tab bar, if omitted, the title of the active window will be used instead.
new_tab my tab
cd somewhere
# Set the layouts allowed in this tab
enabled_layouts tall,stack
# Set the current layout
layout stack
launch zsh
# Create a new OS window
# Any definitions specified before the first new_os_window will apply to first OS window.
new_os_window
# Set new window size to 80x24 cells
os_window_size 80c 24c
# Set the --title for the new OS window
os_window_title my fancy os window
# Set the --class for the new OS window
os_window_class mywindow
# Set the --name for the new OS window
os_window_name myname
# Change the OS window state to normal, fullscreen, maximized or minimized
os_window_state normal
launch sh
# Resize the current window (see the resize_window action for details)
resize_window wider 2
# Make the current window the active (focused) window in its tab
focus
# Make the current OS Window the globally active window
focus_os_window
launch emacs
# Create another tab
new_tab logs
launch tail -f /var/log/syslog
# Focus the first tab (index 0) when the session loads
# You can also use a match expression like: focus_tab title:logs
focus_tab 0
# Create a complex layout using multiple splits. Creates two columns of
# windows with two windows in each column. The windows in the first column are
# split 50:50. In the second column the windows are not evenly split.
new_tab complex tab
layout splits
# First window, set a user variable on it so we can focus it later
launch --var window=first
# Create the second column by splitting the first window vertically
launch --location=vsplit
# Create the third window in the second column by splitting the second window horizontally
# Make it take 40% of the height instead of 50%
launch --location=hsplit --bias=40
# Go back to focusing the first window, so that we can split it
focus_matching_window var:window=first
# Create the final window in the first column
launch --location=hsplit
.. note::
The :doc:`launch <launch>` command when used in a session file cannot create
new OS windows, or tabs.
.. note::
Environment variables of the form :code:`${NAME}` or :code:`$NAME` are
expanded in the session file, except in the *arguments* (not options) to the
launch command. For example:
.. code-block:: sh
launch --cwd=$THIS_IS_EXPANDED some-program $THIS_IS_NOT_EXPANDED
Making newly created windows join an existing session
---------------------------------------------------------
Normally, after activating a session, if you create new windows/tabs
they don't belong to the session. If you would prefer to have them belong
to the currently active session, you can use the :ac:`new_window_with_cwd`
and :ac:`new_tab_with_cwd` actions instead, like this::
map kitty_mod+enter new_window_with_cwd
map kitty_mod+t new_tab_with_cwd
map kitty_mod+n new_os_window_with_cwd
This will cause newly created windows and tabs to belong to the currently active
session, if any. Note that adding a window to a session in this way is
temporary, it does not edit the session file. If you wish to update the
session file of the currently active session, you can use the following
mapping for it::
map f5 save_as_session --relocatable --use-foreground-process --match=session:. .
The two can be combined, using the :ac:`combine` action.
For even more control of what session a window is added to use
the :doc:`launch <launch>` command with the :option:`launch --add-to-session`
flag.
Sessions with remote connections
-------------------------------------
If you use the :doc:`ssh kitten </kittens/ssh>` to connect to remote computers,
:ac:`save_as_session` is smart enough to save the ssh kitten invocation to your
session file, preserving the remote working directory and even the currently
running program on the remote host! Try it, run kitty with::
kitty -o 'map f1 save_as_session --use-foreground-process --relocatable' --session <(echo "layout vertical\nlaunch\nlaunch")
Now in both windows, run::
kitten ssh localhost
To connect them both to a remote computer (replace ``localhost`` with another
computer if you like). In one window change the directory to /tmp and in the
other start some program. Then press :kbd:`F1` to save the session file.
When you run the session file in another kitty instance you will see both
windows re-created, as expected with the correct working directories and
running programs.
Managing multi tab sessions in a single OS Window
----------------------------------------------------
The natural way to organise sessions in kitty is one per :term:`os_window`.
However, if you prefer to manage multiple sessions in a single OS Window, you
can configure the kitty tab bar to only show tabs that belong to the currently
active session. To do so, use :opt:`tab_bar_filter` in :file:`kitty.conf` set::
tab_bar_filter session:~ or session:^$
This will restrict the tab bar to only showing tabs from the currently active
session as well tabs that do not belong to any session. Furthermore, when you
are in a window or tab that does not belong to any session, the tab bar will
show the tabs from the most recent active session, to maintain context.
Keyword reference
---------------------
Below is the list of all supported keywords in session files along with
documentation for them.
``cd [path]``
Change the working directory for all windows in the current tab to
``path``. Relative paths are resolved with respect to the directory
containing the session file.
``focus``
Give keyboard focus to the window created by the previous launch command
``focus_matching_window``
Give keyboard focus to window that matches the specified expression. See
:ref:`search_syntax` for the syntax for matching expressions.
``focus_os_window``
Give keyboard focus to the current OS Window. This is guaranteed to work
only is some other OS Window in the current kitty process has focus,
otherwise the window manager might block changing focus to prevent *focus
stealing*.
``focus_tab [tab specifier]``
Set which tab should be active (focused) in the current OS Window. The tab
specifier can be either a plain number (treated as a 0-based index) or a
match expression. For example, ``focus_tab 0`` will focus the first tab,
``focus_tab 1`` the second tab, and ``focus_tab title:logs`` will focus the
tab whose title matches "logs". See :ref:`search_syntax` for the full syntax
of match expressions. This is useful for session files that create multiple
tabs and want to ensure a specific tab is active when the session is loaded.
``enabled_layouts comma separated list of layout names``
Set the layouts allowed in the current tab. Same syntax as
:opt:`enabled_layouts`.
``launch``
Create a new window running the specified command or the default shell if
no command is specified. See :doc:`launch` for details. Note that creating
tabs and OS Windows using launch is not supported in session files, use the
dedicated keywords for these.
``layout name``
Set the layout for the current tab to the specified layout, including any
specified options, see :doc:`layouts` for the available layouts and
options.
``new_os_window``
Create a new OS Window. Any OS window related keywords specified before the
first ``new_os_window`` will apply to the first OS Window.
``new_tab [tab title]``
Create a new tab with the specified title. If no title is specified, the
title behaves just as for a regular tab in kitty.
``os_window_title``
Set the title for the current OS Window. The OS Window will then always
have this title, it will not change based on the title of the currently active
window inside the OS Window.
``os_window_class``
Set the class part of WM_CLASS or Wayland Application Id for the current OS Window
``os_window_name``
Set the name part of WM_CLASS or Wayland Window tag for the current OS Window
``os_window_size``
Set the size of the current OS Window, can be specified in pixels or cells.
For example: 80c 24c is a window of width 80 cells by 24 cells.
``os_window_state``
Set the state of the current OS Window, can be: ``normal``, ``fullscreen``, ``maximized`` or ``minimized``
``resize_window``
Resize the current window. See the :ac:`resize_window` action for details.
For example: resize_window wider 2
``set_layout_state``
This keyword is only used in session files generated by the
:ac:`save_as_session` action, it's syntax is undocumented and for internal
use only.
``title``
Set the title for the next window. Deprecated, use ``launch --title``
instead.
.. _save_as_session:
The save_as_session action
------------------------------
This action can be mapped to a key press in :file:`kitty.conf`. It will save
the currently open OS Windows, tabs, windows, running programs, working
directories, etc. into a session file. It is a convenient way to
:ref:`complex_sessions`. The options this action takes are documented below.
.. include:: generated/save-as-session.rst

View File

@@ -266,7 +266,7 @@ over SSH when using :doc:`kittens/ssh`.
The :command:`clone-in-kitty` command takes almost all the same arguments as the
:doc:`launch <launch>` command, so you can open a new tab instead or a new OS
window, etc. Arguments of launch that that don't
window, etc. Arguments of launch that can cause code execution or that don't
make sense when cloning are ignored. Most prominently, the following options are
ignored: :option:`--allow-remote-control <launch --allow-remote-control>`,
:option:`--copy-cmdline <launch --copy-cmdline>`, :option:`--copy-env <launch
@@ -313,9 +313,7 @@ window, etc. Not all arguments are supported, see the discussion in the
:ref:`clone_shell` section above.
In order to avoid remote code execution, kitty will only execute the configured
editor and pass the file path to edit to it and it will strip all environment
variables from the :command:`edit-in-kitty` command line. Additionally, parsing
of colors is more limited, reading colors from config files is not allowed.
editor and pass the file path to edit to it.
.. note:: To edit files using sudo the best method is to set the
:code:`SUDO_EDITOR` environment variable to ``kitten edit-in-kitty`` and
@@ -470,21 +468,13 @@ to control its behavior, separated by semi-colons. They are:
this tells kitty that the secondary (PS2) prompt is starting at the
current line.
``click_events=1|2``
``click_events=1``
this tells kitty that the shell is capable of handling
mouse click events. kitty will thus send a click event to the shell when
the user clicks somewhere in the prompt. The shell can then move the cursor
to that position or perform some other appropriate action. Without this,
kitty will instead generate a number of fake key events to move the cursor
to the clicked location, which is not fully robust. A value of ``1`` will
cause the click events to have absolute y co-ordinates, a value of ``2``
will cause them to have y-coordinates relative to the top line of the
current prompt. In relative mode, y is zero for cells on the top line of
the current prompt. The current prompt here is either the secondary (PS2) or
primary prompt. If the secondary prompt is on the same line or above the
mouse position, then the reported y will be with respect to that, otherwise
with respect to the primary prompt. The click event is encoded in the SGR
encoding from xterm.
to the clicked location, which is not fully robust.
kitty also optionally supports sending the cmdline going to be executed with ``<OSC>133;C`` as:

View File

@@ -77,9 +77,9 @@ There are only a handful of metadata keys, defined in the table below:
"d", "Integer from 0 to 15", "0", "The denominator for the fractional scale. Must be ``> n`` when non-zero."
"v", "Integer from 0 to 2", "0", "The vertical alignment to use for fractionally scaled text (n < d). ``0`` - top, ``1`` - bottom, ``2`` - centered"
"v", "Integer from 0 to 2", "0", "The vertical alignment to use for fractionally scaled text. ``0`` - top, ``1`` - bottom, ``2`` - centered"
"h", "Integer from 0 to 2", "0", "The horizontal alignment to use for fractionally scaled text (n < d). ``0`` - left, ``1`` - right, ``2`` - centered"
"h", "Integer from 0 to 2", "0", "The horizontal alignment to use for fractionally scaled text. ``0`` - left, ``1`` - right, ``2`` - centered"
How it works
@@ -126,7 +126,7 @@ and trailers)::
Note, in particular, how the last character, the cat emoji, ``🐈`` has ``w=2``.
In practice client applications can assume that terminal emulators get the
width of all ASCII code points correct and use the ``w=0`` form for efficient
width of all ASCII characters correct and use the ``w=0`` form for efficient
transmission, so that the above becomes::
cool- w=2:🐈
@@ -160,11 +160,8 @@ The fractional scale **does not** affect the number of cells the text occupies,
instead, it just adjusts the rendered font size within those cells.
The fraction is specified using an integer numerator and denominator (``n`` and
``d``). In addition, by using the ``v`` key one can vertically align the
fractionally scaled render area at top, bottom or middle. Similarly, the ``h`` key
does horizontal alignment — left, right or centered. Note that alignment
here is not actual text alignment, it refers to how the fractionally scaled
render area fits inside the full render area of size ``s * w`` by ``s`` cells.
Thus, alignment only applies when ``n < d``.
fractionally scaled text at top, bottom or middle. Similarly, the ``h`` key
does horizontal alignment — left, right or centered.
When using fractional scaling one often wants to fit more than a single
character per cell. To accommodate that, there is the ``w`` key. This specifies
@@ -258,17 +255,17 @@ Detecting if the terminal supports this protocol
-----------------------------------------------------
To detect support for this protocol use the `CPR (Cursor Position Report)
<https://vt100.net/docs/vt510-rm/CPR.html>`__ escape code. Send a ``CR``
(carriage return) followed by ``CPR`` followed by ``\e]_text_size_code;w=2; \a``
which will draw a space character in two cells, followed by another ``CPR``.
Then send ``\e]_text_size_code;s=2; \a`` which will draw a space in a ``2 by 2``
block of cells, followed by another ``CPR``.
<https://vt100.net/docs/vt510-rm/CPR.html>`__ escape code. Send a ``CPR``
followed by ``\e]_text_size_code;w=2; \a`` which will draw a space character in
two cells, followed by another ``CPR``. Then send ``\e]_text_size_code;s=2; \a``
which will draw a space in a ``2 by 2`` block of cells, followed by another
``CPR``.
Then wait for the three responses from the terminal to the three CPR queries.
If the cursor position in the three responses is the same, the terminal does
not support this protocol at all, if the second response has the cursor
moved by two cells, then the width part is supported and if the third response has the
cursor moved by another two cells, then the scale part is supported.
not support this protocol at all, if the second response has a different cursor
position then the width part is supported and if the third response has yet
another position, the scale part is supported.
Interaction with other terminal controls
@@ -360,11 +357,11 @@ The algorithm for splitting text into cells
<https://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakTest.txt>`__.
.. warning::
This algorithm is under public discussion in :iss:`8533`. If serious issues
are brought to light in that discussion, there may be small changes to the
algorithm to address them. Additionally, in the future if the Unicode standard
changes in ways that affect this algorithm, it will be updated. Currently the
algorithm is based on Unicode version 16.
This algorithm is under public discussion in :iss:`8533`. Until that issue
is closed, it is subject to change based on feedback from the community.
Additionally, in the future if the Unicode standard changes in ways that
affect this algorithm, it will be updated. Currently the algorithm is based
on Unicode version 16.
Here, we specify how a terminal must split up text into cells, where a cell is
a width one unit in the character grid the terminal displays.
@@ -372,24 +369,16 @@ a width one unit in the character grid the terminal displays.
The basis for the algorithm is the
`Grapheme segmentation algorithm <https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries>`__
from the Unicode standard. However, that algorithm alone is insufficient to
fully specify text handling for terminals. The full algorithm is specified below.
fully specify text handling for terminals. The full algorithm is specified
below. When a terminal receives a Unicode character:
A terminal using this algorithm must decode the bytes they receive
into Unicode scalar values (i.e., code points except surrogates) using UTF-8.
When it encounters any UTF-8 ill-formed subsequences,
it must be replace each
`maximal subpart of the ill-formed subsequence <https://www.unicode.org/versions/Unicode16.0.0/core-spec/chapter-3/#G66453>`__
with a :code:`U+FFFD REPLACEMENT CHARACTER` (<28>).
#. First check if the character is an ASCII control code, and handle it
appropriately. ASCII control codes are the characters less than 32 and the
character 127 (DEL). The NUL character (0) must be discarded.
For each decoded code point:
#. First check if the code point is an ASCII control code, and handle it
appropriately. ASCII control codes are the code points less than :code:`U+0032` and the
code point :code:`U+0127 DEL`. The code point :code:`U+0000 NUL` must be discarded.
#. Next, check if the code point is *invalid*, and if it is, discard it
and finish processing. Invalid code points are code points with Unicode category :code:`Cc or Cs`
and 66 additional code points: :code:`[0xfdd0, 0xfdef]`, :code:`[0xfffe, 0x10ffff-1, 0x10000]`
#. Next, check if the character is *invalid*, and if it is, discard it
and finish processing. Invalid characters are characters with Unicode category :code:`Cc or Cs`
and 66 additional characters: :code:`[0xfdd0, 0xfdef]`, :code:`[0xfffe, 0x10ffff-1, 0x10000]`
and :code:`[0xffff, 0x10ffff, 0x10000]`.
#. Next, check if there is a previous cell before the
@@ -398,31 +387,30 @@ For each decoded code point:
the last cell of the previous line, provided there is no line break
between the previous and current lines.
#. Next, calculate the width in cells of the received code point,
which can be 0, 1, or 2 depending on the code point properties in
#. Next, calculate the width in cells of the received
character, which can be 0, 1, or 2 depending on the character properties in
the Unicode standard.
#. If there is no previous cell and the code point's width is zero,
the code point is discarded and its processing is finished.
#. If there is no previous cell and the character width is zero, the character
is discarded and processing of the character is finished.
#. If there is a previous cell, the
`Grapheme segmentation algorithm UAX29-C1-1 <https://www.unicode.org/reports/tr29/#C1-1>`__
is used to determine if there is a grapheme boundary between the previous cell
and the current code point.
is used to determine if there is a grapheme boundary between the previous cell and the current character.
#. If there is no boundary, the current code point is added to the previous
cell and processing of the code point is finished. See the :ref:`var_select`
#. If there is no boundary the current character is added to the previous
cell and processing of the character is finished. See the :ref:`var_select`
section below for handling of Unicode Variation selectors.
#. If there is a boundary, but the width of the current code point is zero,
#. If there is a boundary, but the width of the current character is zero
it is added to the previous cell and processing is finished.
#. The code point is added to the current cell and the cursor is moved forward
(right) by either 1 or 2 cells depending on the width of the code point.
#. The character is added to the current cell and the cursor is moved forward
(right) by either 1 or 2 cells depending on the width of the character.
It remains to specify how to calculate the width in cells of a code point.
To do this, code points are divided into various classes, as
It remains to specify how to calculate the width in cells of a Unicode
character. To do this, characters are divided into various classes, as
described by the rules below, in order of decreasing priority:
.. note::
@@ -430,50 +418,49 @@ described by the rules below, in order of decreasing priority:
to :code:`stop` in increments of :code:`step`. When the step is not
specified, it defaults to one.
#. *Regional indicators*: 26 code points starting at :code:`0x1F1E6`. These all
#. *Regional indicators*: 26 characters starting at :code:`0x1F1E6`. These all
have width 2
#. *Doublewidth*: Parse `EastAsianWidth.txt
<https://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt>`__ from
the Unicode standard. All code points marked :code:`W` or :code:`F` have
width two. All code points in the following ranges have width two *unless*
the Unicode standard. All characters marked :code:`W` or :code:`F` have
width two. All characters in the following ranges have width two *unless*
they are marked as :code:`A` in :code:`EastAsianWidth.txt`: :code:`[0x3400,
0x4DBF], [0x4E00, 0x9FFF], [0xF900, 0xFAFF], [0x20000, 0x2FFFD], [0x30000, 0x3FFFD]`
#. *Wide emoji*: Parse `emoji-sequences.txt
<https://www.unicode.org/Public/emoji/latest/emoji-sequences.txt>`__ from
the Unicode standard. All :code:`Basic_Emoji` have width two unless they are
followed by :code:`FE0F` in the file. The leading codepoints in all
followed by :code:`FE0F` in the file. The leading copdepoints in all
:code:`RGI_Emoji_Modifier_Sequence` and :code:`RGI_Emoji_Tag_Sequence` have width two.
All code points in :code:`RGI_Emoji_Flag_Sequence` have width two.
All codepoints in :code:`RGI_Emoji_Flag_Sequence` have width two.
#. *Marks*: These are all zero width code points. They are code points with Unicode
#. *Marks*: These are all zero width characters. They are characters with Unicode
categories whose first letter is :code:`M` or :code:`S`. Additionally,
code points with Unicode category: :code:`Cf`. Finally, they include
all modifier code points from :code:`RGI_Emoji_Modifier_Sequence` in the
characters with Unicode category: :code:`Cf`. Finally, they include
all modifier codepoints from :code:`RGI_Emoji_Modifier_Sequence` in the
*Wide emoji* rule above.
#. All remaining code points have a width of one cell.
#. All remaining codepoints have a width of one cell.
.. _var_select:
Unicode variation selectors
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
There are two code points (:code:`U+FE0E` and :code:`U+FE0F`) that can actually
`alter the width of the previous code point <https://www.unicode.org/reports/tr11/#ED4>`__
by changing their presentation between ``Emoji_Presentation`` and ``Text_Presentation``.
When adding a code point to the previous cell these have to be handled specially.
There are two codepoints (:code:`U+FE0E` and :code:`U+FE0F`) that can actually
alter the width of the previous codepoint. When adding a codepoint to the
previous cell these have to be handled specially.
``U+FE0E`` - Variation Selector 15
When the previous cell has width two and the last code point in the previous
cell is one of the ``Basic_Emoji`` code points from the *Wide emoji* rule above
When the previous cell has width two and the last character in the previous
cell is one of the ``Basic_Emoji`` codepoints from the *Wide emoji* rule above
that is *not* followed by ``FEOF`` then the width of the previous cell is
decreased to one.
``U+FE0F`` - Variation Selector 16
When the previous cell has width one and the last code point in the previous
cell is one of the ``Basic_Emoji`` code points from the *Wide emoji* rule above
When the previous cell has width one and the last character in the previous
cell is one of the ``Basic_Emoji`` codepoints from the *Wide emoji* rule above
that is followed by ``FEOF`` then the width of the
previous cell is increased to two.
@@ -486,34 +473,4 @@ cell wide but it is *not* moved back to the previous line.
To avoid this issue, it is recommended applications detect when ``U+FE0E`` is
present and in such cases use the width part of the text sizing protocol
to control rendering. Alternately, applications can segment text into graphemes
and given a list of graphemes and their widths it is possible to write a simple
function that will output them to the terminal so as to avoid this issue. Below
is such a function in the Python language:
.. code-block:: python
class Grapheme:
text: str
width: int
def output_one_line(iterator_over_graphemes):
' Output graphemes so that they affect exactly wcswidth cells only (works for >= 2 graphemes) '
graphemes = tuple(iterator_over_graphemes)
if not graphemes:
return
yield graphemes[0].text
for i in range(1, len(graphemes)):
g = graphemes[i]
if g is graphemes[-1]:
prev_g = graphemes[i-1]
yield f'\x1b[{prev_g.width}D' # move cursor back
yield g.text
yield f'\x1b[{g.width}D' # move cursor back
yield f'\x1b[{prev_g.width}@' # insert cells
yield prev_g.text
yield f'\x1b[{g.width}C' # move cursor forward
Then, using ``wcswidth()`` the application can split long text into lines of
width no more than the screen width and use the above function to write each
line to the terminal robustly.
to control rendering.

View File

@@ -1,82 +0,0 @@
Wide gamut color formats
=========================
kitty supports modern wide gamut color formats for precise color specification.
These formats can be used anywhere a color value is accepted in the configuration
(foreground, background, color0-color255, etc.).
OKLCH Colors
------------
OKLCH is a perceptually uniform color space, ideal for creating color themes.
The format is::
foreground oklch(0.9 0.05 140)
color1 oklch(0.7 0.25 25)
Parameters:
- **L** (Lightness): 0 to 1, where 0 is black and 1 is white
- **C** (Chroma): 0 to approximately 0.4, represents color saturation
- **H** (Hue): 0 to 360 degrees (0=red, 120=green, 240=blue)
Benefits:
- Perceptually uniform - equal changes produce equal perceived differences
- Adjusting lightness preserves hue (unlike HSL)
- Industry standard for modern color design
Example::
foreground oklch(0.9 0.05 140)
color1 oklch(0.65 0.25 29) # Vibrant red-orange
color2 oklch(0.65 0.25 142) # Vibrant green
color3 oklch(0.70 0.19 90) # Warm yellow
CIE LAB Colors
--------------
CIE LAB is a device-independent color space designed to approximate human vision.
The format is::
background lab(20 5 -10)
color4 lab(50 0 -50)
Parameters:
- **L**: Lightness, 0 to 100 (0 = black, 100 = white)
- **a**: Green (-) to red (+), typically -100 to +100
- **b**: Blue (-) to yellow (+), typically -100 to +100
Example::
background lab(10 0 0) # Very dark neutral gray
foreground lab(90 0 0) # Very light neutral gray
color1 lab(50 60 40) # Red
color4 lab(50 0 -50) # Blue
Gamut Mapping
-------------
When you specify colors in OKLCH or CIE LAB formats that are outside the sRGB
color gamut, kitty automatically converts them using the CSS Color Module Level 4
gamut mapping algorithm:
- Preserves the original lightness and hue as much as possible
- Reduces chroma (saturation) until the color fits within the displayable range
- Uses perceptual color difference (deltaE OK) to minimize visible changes
- Maximizes color saturation while staying in gamut
This ensures that wide gamut colors gracefully degrade on standard sRGB displays while
taking full advantage of wide gamut displays when available. The mapping happens
automatically - you don't need to do anything special.
For example, :code:`oklch(0.7 0.4 25)` might be too saturated for sRGB but will be
automatically adjusted to fit while preserving the perceived hue and lightness.
References
----------
- `CSS Color Module Level 4 <https://www.w3.org/TR/css-color-4/>`_
- `OKLCH Color Space <https://bottosson.github.io/posts/oklab/>`_

View File

@@ -8,9 +8,6 @@ import (
var _ = fmt.Print
//go:embed logo/kitty.png
var KittyLogoAsPNGData []byte
//go:embed kitty_tests/GraphemeBreakTest.json
var grapheme_break_test_data []byte

View File

@@ -34,9 +34,6 @@ def main(args: list[str]=sys.argv) -> None:
elif which == 'cursors':
from gen.cursors import main
main(args)
elif which == 'color-names':
from gen.color_names import main
main(args)
else:
raise SystemExit(f'Unknown which: {which}')

View File

@@ -114,7 +114,6 @@ def generate(
payload_is_base64: bool = True,
start_parsing_at: int = 1,
field_sep: str = ',',
post_init: str = '',
) -> str:
type_map = resolve_keys(keymap)
keys_enum = enum(keymap)
@@ -122,7 +121,6 @@ def generate(
flag_keys = parse_flag(keymap, type_map, command_class)
int_keys, uint_keys = parse_number(keymap)
report_cmd = cmd_for_report(report_name, keymap, type_map, payload_allowed, payload_is_base64)
post_init_line = f'\n {post_init}' if post_init else ''
extra_init = ''
if payload_allowed:
payload_after_value = "case ';': state = PAYLOAD; break;"
@@ -165,7 +163,7 @@ static inline void
{extra_init}
enum PARSER_STATES {{ KEY, EQUAL, UINT, INT, FLAG, AFTER_VALUE {payload} }};
enum PARSER_STATES state = KEY, value_state = FLAG;
{command_class} g = {{0}};{post_init_line}
{command_class} g = {{0}};
unsigned int i, code;
uint64_t lcode; int64_t accumulator;
bool is_negative; (void)is_negative;
@@ -313,13 +311,11 @@ def parsers() -> None:
'U': ('unicode_placement', 'uint'),
'P': ('parent_id', 'uint'),
'Q': ('parent_placement_id', 'uint'),
'N': ('usage_hints', 'uint'),
'H': ('offset_from_parent_x', 'int'),
'V': ('offset_from_parent_y', 'int'),
}
text = generate('parse_graphics_code', 'screen_handle_graphics_command', 'graphics_command', keymap, 'GraphicsCommand')
write_header(text, 'kitty/parse-graphics-command.h')
keymap = {
'w': ('width', 'uint'),
's': ('scale', 'uint'),
@@ -333,21 +329,6 @@ def parsers() -> None:
payload_is_base64=False, start_parsing_at=0, field_sep=':')
write_header(text, 'kitty/parse-multicell-command.h')
keymap = {
't': ('type', flag('aAmMrRopPqeEk')),
'm': ('more', 'uint'),
'i': ('client_id', 'uint'),
'o': ('operation', 'uint'),
'x': ('cell_x', 'int'),
'y': ('cell_y', 'int'),
'X': ('pixel_x', 'int'),
'Y': ('pixel_y', 'int'),
}
text = generate(
'parse_dnd_code', 'screen_handle_dnd_command', 'dnd_command', keymap, 'DnDCommand',
payload_is_base64=False, start_parsing_at=0, field_sep=':')
write_header(text, 'kitty/parse-dnd-command.h')
def main(args: list[str]=sys.argv) -> None:
parsers()

View File

@@ -1,794 +0,0 @@
#!./kitty/launcher/kitty +launch
# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>
import os
import subprocess
import sys
import tempfile
if __name__ == '__main__' and not __package__:
import __main__
__main__.__package__ = 'gen'
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
def Color(r: int, g: int, b: int) -> int:
return (r << 16) | (g << 8) | b
# BEGIN_DATA_SECTION {{{
color_names = {
'alice blue': Color(240, 248, 255),
'aliceblue': Color(240, 248, 255),
'antique white': Color(250, 235, 215),
'antiquewhite': Color(250, 235, 215),
'antiquewhite1': Color(255, 239, 219),
'antiquewhite2': Color(238, 223, 204),
'antiquewhite3': Color(205, 192, 176),
'antiquewhite4': Color(139, 131, 120),
'aquamarine': Color(127, 255, 212),
'aquamarine1': Color(127, 255, 212),
'aquamarine2': Color(118, 238, 198),
'aquamarine3': Color(102, 205, 170),
'aquamarine4': Color(69, 139, 116),
'azure': Color(240, 255, 255),
'azure1': Color(240, 255, 255),
'azure2': Color(224, 238, 238),
'azure3': Color(193, 205, 205),
'azure4': Color(131, 139, 139),
'beige': Color(245, 245, 220),
'bisque': Color(255, 228, 196),
'bisque1': Color(255, 228, 196),
'bisque2': Color(238, 213, 183),
'bisque3': Color(205, 183, 158),
'bisque4': Color(139, 125, 107),
'black': Color(0, 0, 0),
'blanched almond': Color(255, 235, 205),
'blanchedalmond': Color(255, 235, 205),
'blue': Color(0, 0, 255),
'blue violet': Color(138, 43, 226),
'blue1': Color(0, 0, 255),
'blue2': Color(0, 0, 238),
'blue3': Color(0, 0, 205),
'blue4': Color(0, 0, 139),
'blueviolet': Color(138, 43, 226),
'brown': Color(165, 42, 42),
'brown1': Color(255, 64, 64),
'brown2': Color(238, 59, 59),
'brown3': Color(205, 51, 51),
'brown4': Color(139, 35, 35),
'burlywood': Color(222, 184, 135),
'burlywood1': Color(255, 211, 155),
'burlywood2': Color(238, 197, 145),
'burlywood3': Color(205, 170, 125),
'burlywood4': Color(139, 115, 85),
'cadet blue': Color(95, 158, 160),
'cadetblue': Color(95, 158, 160),
'cadetblue1': Color(152, 245, 255),
'cadetblue2': Color(142, 229, 238),
'cadetblue3': Color(122, 197, 205),
'cadetblue4': Color(83, 134, 139),
'chartreuse': Color(127, 255, 0),
'chartreuse1': Color(127, 255, 0),
'chartreuse2': Color(118, 238, 0),
'chartreuse3': Color(102, 205, 0),
'chartreuse4': Color(69, 139, 0),
'chocolate': Color(210, 105, 30),
'chocolate1': Color(255, 127, 36),
'chocolate2': Color(238, 118, 33),
'chocolate3': Color(205, 102, 29),
'chocolate4': Color(139, 69, 19),
'coral': Color(255, 127, 80),
'coral1': Color(255, 114, 86),
'coral2': Color(238, 106, 80),
'coral3': Color(205, 91, 69),
'coral4': Color(139, 62, 47),
'cornflower blue': Color(100, 149, 237),
'cornflowerblue': Color(100, 149, 237),
'cornsilk': Color(255, 248, 220),
'cornsilk1': Color(255, 248, 220),
'cornsilk2': Color(238, 232, 205),
'cornsilk3': Color(205, 200, 177),
'cornsilk4': Color(139, 136, 120),
'cyan': Color(0, 255, 255),
'cyan1': Color(0, 255, 255),
'cyan2': Color(0, 238, 238),
'cyan3': Color(0, 205, 205),
'cyan4': Color(0, 139, 139),
'dark blue': Color(0, 0, 139),
'dark cyan': Color(0, 139, 139),
'dark goldenrod': Color(184, 134, 11),
'dark gray': Color(169, 169, 169),
'dark green': Color(0, 100, 0),
'dark grey': Color(169, 169, 169),
'dark khaki': Color(189, 183, 107),
'dark magenta': Color(139, 0, 139),
'dark olive green': Color(85, 107, 47),
'dark orange': Color(255, 140, 0),
'dark orchid': Color(153, 50, 204),
'dark red': Color(139, 0, 0),
'dark salmon': Color(233, 150, 122),
'dark sea green': Color(143, 188, 143),
'dark slate blue': Color(72, 61, 139),
'dark slate gray': Color(47, 79, 79),
'dark slate grey': Color(47, 79, 79),
'dark turquoise': Color(0, 206, 209),
'dark violet': Color(148, 0, 211),
'darkblue': Color(0, 0, 139),
'darkcyan': Color(0, 139, 139),
'darkgoldenrod': Color(184, 134, 11),
'darkgoldenrod1': Color(255, 185, 15),
'darkgoldenrod2': Color(238, 173, 14),
'darkgoldenrod3': Color(205, 149, 12),
'darkgoldenrod4': Color(139, 101, 8),
'darkgray': Color(169, 169, 169),
'darkgreen': Color(0, 100, 0),
'darkgrey': Color(169, 169, 169),
'darkkhaki': Color(189, 183, 107),
'darkmagenta': Color(139, 0, 139),
'darkolivegreen': Color(85, 107, 47),
'darkolivegreen1': Color(202, 255, 112),
'darkolivegreen2': Color(188, 238, 104),
'darkolivegreen3': Color(162, 205, 90),
'darkolivegreen4': Color(110, 139, 61),
'darkorange': Color(255, 140, 0),
'darkorange1': Color(255, 127, 0),
'darkorange2': Color(238, 118, 0),
'darkorange3': Color(205, 102, 0),
'darkorange4': Color(139, 69, 0),
'darkorchid': Color(153, 50, 204),
'darkorchid1': Color(191, 62, 255),
'darkorchid2': Color(178, 58, 238),
'darkorchid3': Color(154, 50, 205),
'darkorchid4': Color(104, 34, 139),
'darkred': Color(139, 0, 0),
'darksalmon': Color(233, 150, 122),
'darkseagreen': Color(143, 188, 143),
'darkseagreen1': Color(193, 255, 193),
'darkseagreen2': Color(180, 238, 180),
'darkseagreen3': Color(155, 205, 155),
'darkseagreen4': Color(105, 139, 105),
'darkslateblue': Color(72, 61, 139),
'darkslategray': Color(47, 79, 79),
'darkslategray1': Color(151, 255, 255),
'darkslategray2': Color(141, 238, 238),
'darkslategray3': Color(121, 205, 205),
'darkslategray4': Color(82, 139, 139),
'darkslategrey': Color(47, 79, 79),
'darkturquoise': Color(0, 206, 209),
'darkviolet': Color(148, 0, 211),
'debianred': Color(215, 7, 81),
'deep pink': Color(255, 20, 147),
'deep sky blue': Color(0, 191, 255),
'deeppink': Color(255, 20, 147),
'deeppink1': Color(255, 20, 147),
'deeppink2': Color(238, 18, 137),
'deeppink3': Color(205, 16, 118),
'deeppink4': Color(139, 10, 80),
'deepskyblue': Color(0, 191, 255),
'deepskyblue1': Color(0, 191, 255),
'deepskyblue2': Color(0, 178, 238),
'deepskyblue3': Color(0, 154, 205),
'deepskyblue4': Color(0, 104, 139),
'dim gray': Color(105, 105, 105),
'dim grey': Color(105, 105, 105),
'dimgray': Color(105, 105, 105),
'dimgrey': Color(105, 105, 105),
'dodger blue': Color(30, 144, 255),
'dodgerblue': Color(30, 144, 255),
'dodgerblue1': Color(30, 144, 255),
'dodgerblue2': Color(28, 134, 238),
'dodgerblue3': Color(24, 116, 205),
'dodgerblue4': Color(16, 78, 139),
'firebrick': Color(178, 34, 34),
'firebrick1': Color(255, 48, 48),
'firebrick2': Color(238, 44, 44),
'firebrick3': Color(205, 38, 38),
'firebrick4': Color(139, 26, 26),
'floral white': Color(255, 250, 240),
'floralwhite': Color(255, 250, 240),
'forest green': Color(34, 139, 34),
'forestgreen': Color(34, 139, 34),
'gainsboro': Color(220, 220, 220),
'ghost white': Color(248, 248, 255),
'ghostwhite': Color(248, 248, 255),
'gold': Color(255, 215, 0),
'gold1': Color(255, 215, 0),
'gold2': Color(238, 201, 0),
'gold3': Color(205, 173, 0),
'gold4': Color(139, 117, 0),
'goldenrod': Color(218, 165, 32),
'goldenrod1': Color(255, 193, 37),
'goldenrod2': Color(238, 180, 34),
'goldenrod3': Color(205, 155, 29),
'goldenrod4': Color(139, 105, 20),
'gray': Color(190, 190, 190),
'gray0': Color(0, 0, 0),
'gray1': Color(3, 3, 3),
'gray10': Color(26, 26, 26),
'gray100': Color(255, 255, 255),
'gray11': Color(28, 28, 28),
'gray12': Color(31, 31, 31),
'gray13': Color(33, 33, 33),
'gray14': Color(36, 36, 36),
'gray15': Color(38, 38, 38),
'gray16': Color(41, 41, 41),
'gray17': Color(43, 43, 43),
'gray18': Color(46, 46, 46),
'gray19': Color(48, 48, 48),
'gray2': Color(5, 5, 5),
'gray20': Color(51, 51, 51),
'gray21': Color(54, 54, 54),
'gray22': Color(56, 56, 56),
'gray23': Color(59, 59, 59),
'gray24': Color(61, 61, 61),
'gray25': Color(64, 64, 64),
'gray26': Color(66, 66, 66),
'gray27': Color(69, 69, 69),
'gray28': Color(71, 71, 71),
'gray29': Color(74, 74, 74),
'gray3': Color(8, 8, 8),
'gray30': Color(77, 77, 77),
'gray31': Color(79, 79, 79),
'gray32': Color(82, 82, 82),
'gray33': Color(84, 84, 84),
'gray34': Color(87, 87, 87),
'gray35': Color(89, 89, 89),
'gray36': Color(92, 92, 92),
'gray37': Color(94, 94, 94),
'gray38': Color(97, 97, 97),
'gray39': Color(99, 99, 99),
'gray4': Color(10, 10, 10),
'gray40': Color(102, 102, 102),
'gray41': Color(105, 105, 105),
'gray42': Color(107, 107, 107),
'gray43': Color(110, 110, 110),
'gray44': Color(112, 112, 112),
'gray45': Color(115, 115, 115),
'gray46': Color(117, 117, 117),
'gray47': Color(120, 120, 120),
'gray48': Color(122, 122, 122),
'gray49': Color(125, 125, 125),
'gray5': Color(13, 13, 13),
'gray50': Color(127, 127, 127),
'gray51': Color(130, 130, 130),
'gray52': Color(133, 133, 133),
'gray53': Color(135, 135, 135),
'gray54': Color(138, 138, 138),
'gray55': Color(140, 140, 140),
'gray56': Color(143, 143, 143),
'gray57': Color(145, 145, 145),
'gray58': Color(148, 148, 148),
'gray59': Color(150, 150, 150),
'gray6': Color(15, 15, 15),
'gray60': Color(153, 153, 153),
'gray61': Color(156, 156, 156),
'gray62': Color(158, 158, 158),
'gray63': Color(161, 161, 161),
'gray64': Color(163, 163, 163),
'gray65': Color(166, 166, 166),
'gray66': Color(168, 168, 168),
'gray67': Color(171, 171, 171),
'gray68': Color(173, 173, 173),
'gray69': Color(176, 176, 176),
'gray7': Color(18, 18, 18),
'gray70': Color(179, 179, 179),
'gray71': Color(181, 181, 181),
'gray72': Color(184, 184, 184),
'gray73': Color(186, 186, 186),
'gray74': Color(189, 189, 189),
'gray75': Color(191, 191, 191),
'gray76': Color(194, 194, 194),
'gray77': Color(196, 196, 196),
'gray78': Color(199, 199, 199),
'gray79': Color(201, 201, 201),
'gray8': Color(20, 20, 20),
'gray80': Color(204, 204, 204),
'gray81': Color(207, 207, 207),
'gray82': Color(209, 209, 209),
'gray83': Color(212, 212, 212),
'gray84': Color(214, 214, 214),
'gray85': Color(217, 217, 217),
'gray86': Color(219, 219, 219),
'gray87': Color(222, 222, 222),
'gray88': Color(224, 224, 224),
'gray89': Color(227, 227, 227),
'gray9': Color(23, 23, 23),
'gray90': Color(229, 229, 229),
'gray91': Color(232, 232, 232),
'gray92': Color(235, 235, 235),
'gray93': Color(237, 237, 237),
'gray94': Color(240, 240, 240),
'gray95': Color(242, 242, 242),
'gray96': Color(245, 245, 245),
'gray97': Color(247, 247, 247),
'gray98': Color(250, 250, 250),
'gray99': Color(252, 252, 252),
'green': Color(0, 255, 0),
'green yellow': Color(173, 255, 47),
'green1': Color(0, 255, 0),
'green2': Color(0, 238, 0),
'green3': Color(0, 205, 0),
'green4': Color(0, 139, 0),
'greenyellow': Color(173, 255, 47),
'grey': Color(190, 190, 190),
'grey0': Color(0, 0, 0),
'grey1': Color(3, 3, 3),
'grey10': Color(26, 26, 26),
'grey100': Color(255, 255, 255),
'grey11': Color(28, 28, 28),
'grey12': Color(31, 31, 31),
'grey13': Color(33, 33, 33),
'grey14': Color(36, 36, 36),
'grey15': Color(38, 38, 38),
'grey16': Color(41, 41, 41),
'grey17': Color(43, 43, 43),
'grey18': Color(46, 46, 46),
'grey19': Color(48, 48, 48),
'grey2': Color(5, 5, 5),
'grey20': Color(51, 51, 51),
'grey21': Color(54, 54, 54),
'grey22': Color(56, 56, 56),
'grey23': Color(59, 59, 59),
'grey24': Color(61, 61, 61),
'grey25': Color(64, 64, 64),
'grey26': Color(66, 66, 66),
'grey27': Color(69, 69, 69),
'grey28': Color(71, 71, 71),
'grey29': Color(74, 74, 74),
'grey3': Color(8, 8, 8),
'grey30': Color(77, 77, 77),
'grey31': Color(79, 79, 79),
'grey32': Color(82, 82, 82),
'grey33': Color(84, 84, 84),
'grey34': Color(87, 87, 87),
'grey35': Color(89, 89, 89),
'grey36': Color(92, 92, 92),
'grey37': Color(94, 94, 94),
'grey38': Color(97, 97, 97),
'grey39': Color(99, 99, 99),
'grey4': Color(10, 10, 10),
'grey40': Color(102, 102, 102),
'grey41': Color(105, 105, 105),
'grey42': Color(107, 107, 107),
'grey43': Color(110, 110, 110),
'grey44': Color(112, 112, 112),
'grey45': Color(115, 115, 115),
'grey46': Color(117, 117, 117),
'grey47': Color(120, 120, 120),
'grey48': Color(122, 122, 122),
'grey49': Color(125, 125, 125),
'grey5': Color(13, 13, 13),
'grey50': Color(127, 127, 127),
'grey51': Color(130, 130, 130),
'grey52': Color(133, 133, 133),
'grey53': Color(135, 135, 135),
'grey54': Color(138, 138, 138),
'grey55': Color(140, 140, 140),
'grey56': Color(143, 143, 143),
'grey57': Color(145, 145, 145),
'grey58': Color(148, 148, 148),
'grey59': Color(150, 150, 150),
'grey6': Color(15, 15, 15),
'grey60': Color(153, 153, 153),
'grey61': Color(156, 156, 156),
'grey62': Color(158, 158, 158),
'grey63': Color(161, 161, 161),
'grey64': Color(163, 163, 163),
'grey65': Color(166, 166, 166),
'grey66': Color(168, 168, 168),
'grey67': Color(171, 171, 171),
'grey68': Color(173, 173, 173),
'grey69': Color(176, 176, 176),
'grey7': Color(18, 18, 18),
'grey70': Color(179, 179, 179),
'grey71': Color(181, 181, 181),
'grey72': Color(184, 184, 184),
'grey73': Color(186, 186, 186),
'grey74': Color(189, 189, 189),
'grey75': Color(191, 191, 191),
'grey76': Color(194, 194, 194),
'grey77': Color(196, 196, 196),
'grey78': Color(199, 199, 199),
'grey79': Color(201, 201, 201),
'grey8': Color(20, 20, 20),
'grey80': Color(204, 204, 204),
'grey81': Color(207, 207, 207),
'grey82': Color(209, 209, 209),
'grey83': Color(212, 212, 212),
'grey84': Color(214, 214, 214),
'grey85': Color(217, 217, 217),
'grey86': Color(219, 219, 219),
'grey87': Color(222, 222, 222),
'grey88': Color(224, 224, 224),
'grey89': Color(227, 227, 227),
'grey9': Color(23, 23, 23),
'grey90': Color(229, 229, 229),
'grey91': Color(232, 232, 232),
'grey92': Color(235, 235, 235),
'grey93': Color(237, 237, 237),
'grey94': Color(240, 240, 240),
'grey95': Color(242, 242, 242),
'grey96': Color(245, 245, 245),
'grey97': Color(247, 247, 247),
'grey98': Color(250, 250, 250),
'grey99': Color(252, 252, 252),
'honeydew': Color(240, 255, 240),
'honeydew1': Color(240, 255, 240),
'honeydew2': Color(224, 238, 224),
'honeydew3': Color(193, 205, 193),
'honeydew4': Color(131, 139, 131),
'hot pink': Color(255, 105, 180),
'hotpink': Color(255, 105, 180),
'hotpink1': Color(255, 110, 180),
'hotpink2': Color(238, 106, 167),
'hotpink3': Color(205, 96, 144),
'hotpink4': Color(139, 58, 98),
'indian red': Color(205, 92, 92),
'indianred': Color(205, 92, 92),
'indianred1': Color(255, 106, 106),
'indianred2': Color(238, 99, 99),
'indianred3': Color(205, 85, 85),
'indianred4': Color(139, 58, 58),
'ivory': Color(255, 255, 240),
'ivory1': Color(255, 255, 240),
'ivory2': Color(238, 238, 224),
'ivory3': Color(205, 205, 193),
'ivory4': Color(139, 139, 131),
'khaki': Color(240, 230, 140),
'khaki1': Color(255, 246, 143),
'khaki2': Color(238, 230, 133),
'khaki3': Color(205, 198, 115),
'khaki4': Color(139, 134, 78),
'lavender': Color(230, 230, 250),
'lavender blush': Color(255, 240, 245),
'lavenderblush': Color(255, 240, 245),
'lavenderblush1': Color(255, 240, 245),
'lavenderblush2': Color(238, 224, 229),
'lavenderblush3': Color(205, 193, 197),
'lavenderblush4': Color(139, 131, 134),
'lawn green': Color(124, 252, 0),
'lawngreen': Color(124, 252, 0),
'lemon chiffon': Color(255, 250, 205),
'lemonchiffon': Color(255, 250, 205),
'lemonchiffon1': Color(255, 250, 205),
'lemonchiffon2': Color(238, 233, 191),
'lemonchiffon3': Color(205, 201, 165),
'lemonchiffon4': Color(139, 137, 112),
'light blue': Color(173, 216, 230),
'light coral': Color(240, 128, 128),
'light cyan': Color(224, 255, 255),
'light goldenrod': Color(238, 221, 130),
'light goldenrod yellow': Color(250, 250, 210),
'light gray': Color(211, 211, 211),
'light green': Color(144, 238, 144),
'light grey': Color(211, 211, 211),
'light pink': Color(255, 182, 193),
'light salmon': Color(255, 160, 122),
'light sea green': Color(32, 178, 170),
'light sky blue': Color(135, 206, 250),
'light slate blue': Color(132, 112, 255),
'light slate gray': Color(119, 136, 153),
'light slate grey': Color(119, 136, 153),
'light steel blue': Color(176, 196, 222),
'light yellow': Color(255, 255, 224),
'lightblue': Color(173, 216, 230),
'lightblue1': Color(191, 239, 255),
'lightblue2': Color(178, 223, 238),
'lightblue3': Color(154, 192, 205),
'lightblue4': Color(104, 131, 139),
'lightcoral': Color(240, 128, 128),
'lightcyan': Color(224, 255, 255),
'lightcyan1': Color(224, 255, 255),
'lightcyan2': Color(209, 238, 238),
'lightcyan3': Color(180, 205, 205),
'lightcyan4': Color(122, 139, 139),
'lightgoldenrod': Color(238, 221, 130),
'lightgoldenrod1': Color(255, 236, 139),
'lightgoldenrod2': Color(238, 220, 130),
'lightgoldenrod3': Color(205, 190, 112),
'lightgoldenrod4': Color(139, 129, 76),
'lightgoldenrodyellow': Color(250, 250, 210),
'lightgray': Color(211, 211, 211),
'lightgreen': Color(144, 238, 144),
'lightgrey': Color(211, 211, 211),
'lightpink': Color(255, 182, 193),
'lightpink1': Color(255, 174, 185),
'lightpink2': Color(238, 162, 173),
'lightpink3': Color(205, 140, 149),
'lightpink4': Color(139, 95, 101),
'lightsalmon': Color(255, 160, 122),
'lightsalmon1': Color(255, 160, 122),
'lightsalmon2': Color(238, 149, 114),
'lightsalmon3': Color(205, 129, 98),
'lightsalmon4': Color(139, 87, 66),
'lightseagreen': Color(32, 178, 170),
'lightskyblue': Color(135, 206, 250),
'lightskyblue1': Color(176, 226, 255),
'lightskyblue2': Color(164, 211, 238),
'lightskyblue3': Color(141, 182, 205),
'lightskyblue4': Color(96, 123, 139),
'lightslateblue': Color(132, 112, 255),
'lightslategray': Color(119, 136, 153),
'lightslategrey': Color(119, 136, 153),
'lightsteelblue': Color(176, 196, 222),
'lightsteelblue1': Color(202, 225, 255),
'lightsteelblue2': Color(188, 210, 238),
'lightsteelblue3': Color(162, 181, 205),
'lightsteelblue4': Color(110, 123, 139),
'lightyellow': Color(255, 255, 224),
'lightyellow1': Color(255, 255, 224),
'lightyellow2': Color(238, 238, 209),
'lightyellow3': Color(205, 205, 180),
'lightyellow4': Color(139, 139, 122),
'lime green': Color(50, 205, 50),
'limegreen': Color(50, 205, 50),
'linen': Color(250, 240, 230),
'magenta': Color(255, 0, 255),
'magenta1': Color(255, 0, 255),
'magenta2': Color(238, 0, 238),
'magenta3': Color(205, 0, 205),
'magenta4': Color(139, 0, 139),
'maroon': Color(176, 48, 96),
'maroon1': Color(255, 52, 179),
'maroon2': Color(238, 48, 167),
'maroon3': Color(205, 41, 144),
'maroon4': Color(139, 28, 98),
'medium aquamarine': Color(102, 205, 170),
'medium blue': Color(0, 0, 205),
'medium orchid': Color(186, 85, 211),
'medium purple': Color(147, 112, 219),
'medium sea green': Color(60, 179, 113),
'medium slate blue': Color(123, 104, 238),
'medium spring green': Color(0, 250, 154),
'medium turquoise': Color(72, 209, 204),
'medium violet red': Color(199, 21, 133),
'mediumaquamarine': Color(102, 205, 170),
'mediumblue': Color(0, 0, 205),
'mediumorchid': Color(186, 85, 211),
'mediumorchid1': Color(224, 102, 255),
'mediumorchid2': Color(209, 95, 238),
'mediumorchid3': Color(180, 82, 205),
'mediumorchid4': Color(122, 55, 139),
'mediumpurple': Color(147, 112, 219),
'mediumpurple1': Color(171, 130, 255),
'mediumpurple2': Color(159, 121, 238),
'mediumpurple3': Color(137, 104, 205),
'mediumpurple4': Color(93, 71, 139),
'mediumseagreen': Color(60, 179, 113),
'mediumslateblue': Color(123, 104, 238),
'mediumspringgreen': Color(0, 250, 154),
'mediumturquoise': Color(72, 209, 204),
'mediumvioletred': Color(199, 21, 133),
'midnight blue': Color(25, 25, 112),
'midnightblue': Color(25, 25, 112),
'mint cream': Color(245, 255, 250),
'mintcream': Color(245, 255, 250),
'misty rose': Color(255, 228, 225),
'mistyrose': Color(255, 228, 225),
'mistyrose1': Color(255, 228, 225),
'mistyrose2': Color(238, 213, 210),
'mistyrose3': Color(205, 183, 181),
'mistyrose4': Color(139, 125, 123),
'moccasin': Color(255, 228, 181),
'navajo white': Color(255, 222, 173),
'navajowhite': Color(255, 222, 173),
'navajowhite1': Color(255, 222, 173),
'navajowhite2': Color(238, 207, 161),
'navajowhite3': Color(205, 179, 139),
'navajowhite4': Color(139, 121, 94),
'navy': Color(0, 0, 128),
'navy blue': Color(0, 0, 128),
'navyblue': Color(0, 0, 128),
'old lace': Color(253, 245, 230),
'oldlace': Color(253, 245, 230),
'olive drab': Color(107, 142, 35),
'olivedrab': Color(107, 142, 35),
'olivedrab1': Color(192, 255, 62),
'olivedrab2': Color(179, 238, 58),
'olivedrab3': Color(154, 205, 50),
'olivedrab4': Color(105, 139, 34),
'orange': Color(255, 165, 0),
'orange red': Color(255, 69, 0),
'orange1': Color(255, 165, 0),
'orange2': Color(238, 154, 0),
'orange3': Color(205, 133, 0),
'orange4': Color(139, 90, 0),
'orangered': Color(255, 69, 0),
'orangered1': Color(255, 69, 0),
'orangered2': Color(238, 64, 0),
'orangered3': Color(205, 55, 0),
'orangered4': Color(139, 37, 0),
'orchid': Color(218, 112, 214),
'orchid1': Color(255, 131, 250),
'orchid2': Color(238, 122, 233),
'orchid3': Color(205, 105, 201),
'orchid4': Color(139, 71, 137),
'pale goldenrod': Color(238, 232, 170),
'pale green': Color(152, 251, 152),
'pale turquoise': Color(175, 238, 238),
'pale violet red': Color(219, 112, 147),
'palegoldenrod': Color(238, 232, 170),
'palegreen': Color(152, 251, 152),
'palegreen1': Color(154, 255, 154),
'palegreen2': Color(144, 238, 144),
'palegreen3': Color(124, 205, 124),
'palegreen4': Color(84, 139, 84),
'paleturquoise': Color(175, 238, 238),
'paleturquoise1': Color(187, 255, 255),
'paleturquoise2': Color(174, 238, 238),
'paleturquoise3': Color(150, 205, 205),
'paleturquoise4': Color(102, 139, 139),
'palevioletred': Color(219, 112, 147),
'palevioletred1': Color(255, 130, 171),
'palevioletred2': Color(238, 121, 159),
'palevioletred3': Color(205, 104, 137),
'palevioletred4': Color(139, 71, 93),
'papaya whip': Color(255, 239, 213),
'papayawhip': Color(255, 239, 213),
'peach puff': Color(255, 218, 185),
'peachpuff': Color(255, 218, 185),
'peachpuff1': Color(255, 218, 185),
'peachpuff2': Color(238, 203, 173),
'peachpuff3': Color(205, 175, 149),
'peachpuff4': Color(139, 119, 101),
'peru': Color(205, 133, 63),
'pink': Color(255, 192, 203),
'pink1': Color(255, 181, 197),
'pink2': Color(238, 169, 184),
'pink3': Color(205, 145, 158),
'pink4': Color(139, 99, 108),
'plum': Color(221, 160, 221),
'plum1': Color(255, 187, 255),
'plum2': Color(238, 174, 238),
'plum3': Color(205, 150, 205),
'plum4': Color(139, 102, 139),
'powder blue': Color(176, 224, 230),
'powderblue': Color(176, 224, 230),
'purple': Color(160, 32, 240),
'purple1': Color(155, 48, 255),
'purple2': Color(145, 44, 238),
'purple3': Color(125, 38, 205),
'purple4': Color(85, 26, 139),
'red': Color(255, 0, 0),
'red1': Color(255, 0, 0),
'red2': Color(238, 0, 0),
'red3': Color(205, 0, 0),
'red4': Color(139, 0, 0),
'rosy brown': Color(188, 143, 143),
'rosybrown': Color(188, 143, 143),
'rosybrown1': Color(255, 193, 193),
'rosybrown2': Color(238, 180, 180),
'rosybrown3': Color(205, 155, 155),
'rosybrown4': Color(139, 105, 105),
'royal blue': Color(65, 105, 225),
'royalblue': Color(65, 105, 225),
'royalblue1': Color(72, 118, 255),
'royalblue2': Color(67, 110, 238),
'royalblue3': Color(58, 95, 205),
'royalblue4': Color(39, 64, 139),
'saddle brown': Color(139, 69, 19),
'saddlebrown': Color(139, 69, 19),
'salmon': Color(250, 128, 114),
'salmon1': Color(255, 140, 105),
'salmon2': Color(238, 130, 98),
'salmon3': Color(205, 112, 84),
'salmon4': Color(139, 76, 57),
'sandy brown': Color(244, 164, 96),
'sandybrown': Color(244, 164, 96),
'sea green': Color(46, 139, 87),
'seagreen': Color(46, 139, 87),
'seagreen1': Color(84, 255, 159),
'seagreen2': Color(78, 238, 148),
'seagreen3': Color(67, 205, 128),
'seagreen4': Color(46, 139, 87),
'seashell': Color(255, 245, 238),
'seashell1': Color(255, 245, 238),
'seashell2': Color(238, 229, 222),
'seashell3': Color(205, 197, 191),
'seashell4': Color(139, 134, 130),
'sienna': Color(160, 82, 45),
'sienna1': Color(255, 130, 71),
'sienna2': Color(238, 121, 66),
'sienna3': Color(205, 104, 57),
'sienna4': Color(139, 71, 38),
'sky blue': Color(135, 206, 235),
'skyblue': Color(135, 206, 235),
'skyblue1': Color(135, 206, 255),
'skyblue2': Color(126, 192, 238),
'skyblue3': Color(108, 166, 205),
'skyblue4': Color(74, 112, 139),
'slate blue': Color(106, 90, 205),
'slate gray': Color(112, 128, 144),
'slate grey': Color(112, 128, 144),
'slateblue': Color(106, 90, 205),
'slateblue1': Color(131, 111, 255),
'slateblue2': Color(122, 103, 238),
'slateblue3': Color(105, 89, 205),
'slateblue4': Color(71, 60, 139),
'slategray': Color(112, 128, 144),
'slategray1': Color(198, 226, 255),
'slategray2': Color(185, 211, 238),
'slategray3': Color(159, 182, 205),
'slategray4': Color(108, 123, 139),
'slategrey': Color(112, 128, 144),
'snow': Color(255, 250, 250),
'snow1': Color(255, 250, 250),
'snow2': Color(238, 233, 233),
'snow3': Color(205, 201, 201),
'snow4': Color(139, 137, 137),
'spring green': Color(0, 255, 127),
'springgreen': Color(0, 255, 127),
'springgreen1': Color(0, 255, 127),
'springgreen2': Color(0, 238, 118),
'springgreen3': Color(0, 205, 102),
'springgreen4': Color(0, 139, 69),
'steel blue': Color(70, 130, 180),
'steelblue': Color(70, 130, 180),
'steelblue1': Color(99, 184, 255),
'steelblue2': Color(92, 172, 238),
'steelblue3': Color(79, 148, 205),
'steelblue4': Color(54, 100, 139),
'tan': Color(210, 180, 140),
'tan1': Color(255, 165, 79),
'tan2': Color(238, 154, 73),
'tan3': Color(205, 133, 63),
'tan4': Color(139, 90, 43),
'thistle': Color(216, 191, 216),
'thistle1': Color(255, 225, 255),
'thistle2': Color(238, 210, 238),
'thistle3': Color(205, 181, 205),
'thistle4': Color(139, 123, 139),
'tomato': Color(255, 99, 71),
'tomato1': Color(255, 99, 71),
'tomato2': Color(238, 92, 66),
'tomato3': Color(205, 79, 57),
'tomato4': Color(139, 54, 38),
'turquoise': Color(64, 224, 208),
'turquoise1': Color(0, 245, 255),
'turquoise2': Color(0, 229, 238),
'turquoise3': Color(0, 197, 205),
'turquoise4': Color(0, 134, 139),
'violet': Color(238, 130, 238),
'violet red': Color(208, 32, 144),
'violetred': Color(208, 32, 144),
'violetred1': Color(255, 62, 150),
'violetred2': Color(238, 58, 140),
'violetred3': Color(205, 50, 120),
'violetred4': Color(139, 34, 82),
'wheat': Color(245, 222, 179),
'wheat1': Color(255, 231, 186),
'wheat2': Color(238, 216, 174),
'wheat3': Color(205, 186, 150),
'wheat4': Color(139, 126, 102),
'white': Color(255, 255, 255),
'white smoke': Color(245, 245, 245),
'whitesmoke': Color(245, 245, 245),
'yellow': Color(255, 255, 0),
'yellow green': Color(154, 205, 50),
'yellow1': Color(255, 255, 0),
'yellow2': Color(238, 238, 0),
'yellow3': Color(205, 205, 0),
'yellow4': Color(139, 139, 0),
'yellowgreen': Color(154, 205, 50)}
# END_DATA_SECTION }}}
def main(args: list[str]=sys.argv) -> None:
with tempfile.TemporaryFile(suffix='.gperf', mode='w+') as tf:
print('struct Keyword { int name, value; };\n%%', file=tf)
names = sorted(color_names)
for name in names:
print(f'{name}, {color_names[name]}', file=tf)
print('%%', file=tf)
tf.flush()
tf.seek(0)
with open('kitty/color-names.h', 'w') as header:
subprocess.check_call(
'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 /dev/stdin'
' --word-array-name color_names --pic --compare-strncmp'.split(), stdout=header, stdin=tf)
if __name__ == '__main__':
import runpy
m = runpy.run_path(os.path.dirname(os.path.abspath(__file__)))
m['main']([sys.executable, 'color-names'])

View File

@@ -43,29 +43,17 @@ def main(args: list[str]=sys.argv) -> None:
from kitty.options.definition import definition
nullable_colors = []
all_colors = []
special_colors = []
for opt in definition.iter_all_options():
if callable(opt.parser_func):
match opt.parser_func.__name__:
case 'to_color':
all_colors.append(opt.name)
case 'macos_titlebar_color' | 'titlebar_color' | 'scrollbar_color':
all_colors.append(opt.name)
special_colors.append(opt.name)
case 'to_color_or_none' | 'cursor_text_color':
all_colors.append(opt.name)
nullable_colors.append(opt.name)
if opt.parser_func.__name__ in ('to_color_or_none', 'cursor_text_color'):
nullable_colors.append(opt.name)
all_colors.append(opt.name)
elif opt.parser_func.__name__ in ('to_color', 'titlebar_color', 'macos_titlebar_color'):
all_colors.append(opt.name)
patch_color_list('tools/cmd/at/set_colors.go', nullable_colors, 'NULLABLE')
patch_color_list('tools/themes/collection.go', all_colors, 'ALL')
nc = ',\n '.join(f'{x!r}' for x in nullable_colors)
sc = ',\n '.join(f'{x!r}' for x in special_colors)
ac = ',\n '.join(f'{x!r}' for x in all_colors)
write_output('kitty', definition,
f'\nnullable_colors = frozenset({{\n {nc}\n}})\n'
f'\nspecial_colors = frozenset({{\n {sc}\n}})\n'
f'\nall_colors = frozenset({{\n {ac}\n}})\n'
)
write_output('kitty', definition, f'\nnullable_colors = frozenset({{\n {nc}\n}})\n')
if __name__ == '__main__':

View File

@@ -35,13 +35,13 @@ from kitty.cli import (
from kitty.conf.generate import gen_go_code
from kitty.conf.types import Definition
from kitty.config import commented_out_default_config
from kitty.fast_data_types import all_color_names
from kitty.guess_mime_type import known_extensions, text_mimes
from kitty.key_encoding import config_mod_map
from kitty.key_names import character_key_name_aliases, functional_key_name_aliases
from kitty.options.types import Options
from kitty.rc.base import RemoteCommand, all_command_names, command_for_name
from kitty.remote_control import global_options_spec
from kitty.rgb import color_names
from kitty.simple_cli_definitions import CompletionSpec, parse_option_spec, serialize_as_go_string
if __name__ == '__main__' and not __package__:
@@ -201,7 +201,6 @@ def make_bitfields() -> None:
)
mb('tools/vt', 'CellColor', 'is_idx 1', 'red 8', 'green 8', 'blue 8')
mb('tools/vt', 'LineAttrs', 'prompt_kind 2',)
mb('kittens/choose_files', 'CombinedScore', 'score 16', 'length 16', 'index 32')
# }}}
# Completions {{{
@@ -247,7 +246,7 @@ def clone_safe_launch_opts() -> Sequence[GoOption]:
ans = []
allowed = clone_safe_opts()
for o in go_options_for_seq(parse_option_spec(options_spec())[0]):
if o.obj_defn.name in allowed:
if o.obj_dict['name'] in allowed:
ans.append(o)
return tuple(ans)
@@ -260,9 +259,9 @@ def completion_for_launch_wrappers(*names: str) -> None:
def generate_completions_for_kitty() -> None:
print('package completion\n')
print('import "github.com/kovidgoyal/kitty/tools/cli"')
print('import "github.com/kovidgoyal/kitty/tools/cmd/tool"')
print('import "github.com/kovidgoyal/kitty/tools/cmd/at"')
print('import "kitty/tools/cli"')
print('import "kitty/tools/cmd/tool"')
print('import "kitty/tools/cmd/at"')
print('func kitty(root *cli.Command) {')
@@ -459,7 +458,7 @@ def generate_conf_parser(kitten: str, defn: Definition) -> None:
def generate_extra_cli_parser(name: str, spec: str) -> None:
print('import "github.com/kovidgoyal/kitty/tools/cli"')
print('import "kitty/tools/cli"')
go_opts = tuple(go_options_for_seq(parse_option_spec(spec)[0]))
print(f'type {name}_options struct ''{')
for opt in go_opts:
@@ -509,7 +508,7 @@ def kitten_clis() -> None:
has_underscore = '_' in kitten
print(f'package {kitten}')
print('import "fmt"')
print('import "github.com/kovidgoyal/kitty/tools/cli"')
print('import "kitty/tools/cli"')
print('var _ = fmt.Sprintf')
print('func create_cmd(root *cli.Command, run_func func(*cli.Command, *Options, []string)(int, error)) {')
print('ans := root.AddSubCommand(&cli.Command{')
@@ -585,7 +584,7 @@ def generate_color_names() -> str:
cursor = "" if Options.cursor is None else Options.cursor.as_sharp
return 'package style\n\nvar ColorNames = map[string]RGBA{' + '\n'.join(
f'\t"{name}": RGBA{{ Red:{val.red}, Green:{val.green}, Blue:{val.blue} }},'
for name, val in all_color_names()
for name, val in color_names.items()
) + '\n}' + '\n\nvar ColorTable = [256]uint32{' + ', '.join(
f'{x}' for x in Options.color_table) + '}\n' + f'''
var DefaultColors = struct {{
@@ -613,9 +612,8 @@ def generate_constants() -> str:
from kittens.query_terminal.main import all_queries
from kitty.colors import ThemeFile
from kitty.config import option_names_for_completion
from kitty.fast_data_types import DND_CODE, FILE_TRANSFER_CODE
from kitty.fast_data_types import FILE_TRANSFER_CODE
from kitty.options.utils import allowed_shell_integration_values, url_style_map
from kitty.simple_cli_definitions import CONFIG_HELP
del sys.modules['kittens.hints.main']
del sys.modules['kittens.query_terminal.main']
ref_map = load_ref_map()
@@ -637,7 +635,6 @@ type VersionType struct {{
const VersionString string = "{kc.str_version}"
const WebsiteBaseURL string = "{kc.website_base_url}"
const FileTransferCode int = {FILE_TRANSFER_CODE}
const DndCode int = {DND_CODE}
const ImagePlaceholderChar rune = {placeholder_char}
const SSHControlMasterTemplate = "{kc.ssh_control_master_template}"
const RC_ENCRYPTION_PROTOCOL_VERSION string = "{kc.RC_ENCRYPTION_PROTOCOL_VERSION}"
@@ -649,7 +646,6 @@ const HintsDefaultRegex = `{DEFAULT_REGEX}`
const DefaultTermName = `{Options.term}`
const DefaultUrlStyle = `{url_style}`
const DefaultUrlColor = `{Options.url_color.as_sharp}`
const ConfigHelp = "{serialize_as_go_string(CONFIG_HELP)}"
var Version VersionType = VersionType{{Major: {kc.version.major}, Minor: {kc.version.minor}, Patch: {kc.version.patch},}}
var DefaultPager []string = []string{{ {dp} }}
var FunctionalKeyNameAliases = map[string]string{serialize_go_dict(functional_key_name_aliases)}
@@ -662,7 +658,7 @@ var QueryNames = []string{{ {query_names} }}
var CommentedOutDefaultConfig = "{serialize_as_go_string(commented_out_default_config())}"
var KittyConfigDefaults = struct {{
Term, Shell_integration, Select_by_word_characters, Url_excluded_characters, Shell string
Wheel_scroll_multiplier float64
Wheel_scroll_multiplier int
Url_prefixes []string
}}{{
Term: "{Options.term}", Shell_integration: "{' '.join(Options.shell_integration)}", Url_prefixes: []string{{ {url_prefixes} }},
@@ -727,7 +723,7 @@ def update_at_commands() -> None:
odef = '\n'.join(opt_def)
code = f'''
package at
import "github.com/kovidgoyal/kitty/tools/cli"
import "kitty/tools/cli"
type rc_global_options struct {{
{sdef}
}}
@@ -755,11 +751,10 @@ def update_completion() -> None:
with replace_if_needed('tools/cmd/edit_in_kitty/launch_generated.go'):
print('package edit_in_kitty')
print('import "github.com/kovidgoyal/kitty/tools/cli"')
print('import "kitty/tools/cli"')
print('func AddCloneSafeOpts(cmd *cli.Command) {')
completion_for_launch_wrappers('cmd')
m = ','.join(text_mimes)
print(''.join(CompletionSpec.from_string(f'type:file mime:text/*,{m} ext:bash,zsh group:"Text files"').as_go_code('cmd.ArgCompleter', ' = ')))
print(''.join(CompletionSpec.from_string('type:file mime:text/* group:"Text files"').as_go_code('cmd.ArgCompleter', ' = ')))
print('}')

11101
gen/nerd-fonts-glyphs.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,7 @@ def generate_srgb_lut(line_prefix: str = ' ') -> list[str]:
lines: list[str] = []
for i in range(256):
values.append(f'{to_linear(i / 255.0):1.8f}f')
values.append(f'{to_linear(i / 255.0):1.5f}f')
for i in range(16):
lines.append(line_prefix + ', '.join(values[i * 16:(i + 1) * 16]) + ',')

View File

@@ -14,7 +14,18 @@ from functools import lru_cache, partial
from html.entities import html5
from io import StringIO
from math import ceil, log
from typing import Callable, DefaultDict, Iterator, Literal, NamedTuple, Optional, Protocol, Sequence, TypedDict, TypeVar, Union
from typing import (
Callable,
DefaultDict,
Iterator,
Literal,
NamedTuple,
Optional,
Protocol,
Sequence,
TypedDict,
Union,
)
from urllib.request import urlopen
if __name__ == '__main__' and not __package__:
@@ -32,7 +43,8 @@ if len(non_characters) != 66:
emoji_skin_tone_modifiers = frozenset(range(0x1f3fb, 0x1F3FF + 1))
def fetch_url(url: str) -> str:
def get_data(fname: str, folder: str = 'UCD') -> Iterable[str]:
url = f'https://www.unicode.org/Public/{folder}/latest/{fname}'
bn = os.path.basename(url)
local = os.path.join('/tmp', bn)
if os.path.exists(local):
@@ -42,12 +54,7 @@ def fetch_url(url: str) -> str:
data = urlopen(url).read()
with open(local, 'wb') as f:
f.write(data)
return data.decode()
def get_data(fname: str, folder: str = 'UCD') -> Iterable[str]:
url = f'https://www.unicode.org/Public/{folder}/latest/{fname}'
for line in fetch_url(url).splitlines():
for line in data.decode('utf-8').splitlines():
line = line.strip()
if line and not line.startswith('#'):
yield line
@@ -144,13 +151,13 @@ def parse_ucd() -> None:
# the future.
marks.add(codepoint)
gndata = fetch_url('https://raw.githubusercontent.com/ryanoasis/nerd-fonts/refs/heads/master/glyphnames.json')
for name, val in json.loads(gndata).items():
if name != 'METADATA':
codepoint = int(val['code'], 16)
category, sep, name = name.rpartition('-')
name = name or category
name = name.replace('_', ' ')
with open('gen/nerd-fonts-glyphs.txt') as f:
for line in f:
line = line.strip()
if not line or line.startswith('#'):
continue
code, category, name = line.split(' ', 2)
codepoint = int(code, 16)
if name and codepoint not in name_map:
name_map[codepoint] = name.upper()
for word in name.lower().split():
@@ -455,9 +462,7 @@ def mask_for(bits: int) -> int:
return ~((~0) << bits)
HashableType = TypeVar('HashableType', bound=Hashable)
def splitbins(t: tuple[HashableType, ...], property_size: int, use_fixed_shift: int = 0) -> tuple[list[int], list[int], list[HashableType], int]:
def splitbins[T: Hashable](t: tuple[T, ...], property_size: int, use_fixed_shift: int = 0) -> tuple[list[int], list[int], list[T], int]:
if use_fixed_shift:
candidates = range(use_fixed_shift, use_fixed_shift + 1)
else:
@@ -468,8 +473,8 @@ def splitbins(t: tuple[HashableType, ...], property_size: int, use_fixed_shift:
n >>= 1
maxshift += 1
candidates = range(maxshift + 1)
t3: list[HashableType] = []
tmap: dict[HashableType, int] = {}
t3: list[T] = []
tmap: dict[T, int] = {}
seen = set()
for x in t:
if x not in seen:

View File

@@ -10,8 +10,7 @@ import subprocess
cmdline = (
'glad --out-path {dest} --api gl:core=3.1 '
' --extensions GL_ARB_texture_storage,GL_ARB_copy_image,GL_ARB_multisample,GL_ARB_robustness,'
'GL_ARB_instanced_arrays,GL_KHR_debug,GL_ARB_framebuffer_sRGB,GL_EXT_framebuffer_sRGB '
' --extensions GL_ARB_texture_storage,GL_ARB_copy_image,GL_ARB_multisample,GL_ARB_robustness,GL_ARB_instanced_arrays,GL_KHR_debug '
'c --header-only --debug'
)

32
glfw/backend_utils.c vendored
View File

@@ -31,6 +31,7 @@ update_fds(EventLoopData *eld) {
}
}
static id_type watch_counter = 0;
id_type
addWatch(EventLoopData *eld, const char* name, int fd, int events, int enabled, watch_callback_func cb, void *cb_data) {
@@ -38,7 +39,6 @@ addWatch(EventLoopData *eld, const char* name, int fd, int events, int enabled,
_glfwInputError(GLFW_PLATFORM_ERROR, "Too many watches added");
return 0;
}
static id_type watch_counter = 0;
Watch *w = eld->watches + eld->watches_count++;
w->name = name;
w->fd = fd; w->events = events; w->enabled = enabled;
@@ -234,12 +234,6 @@ mark_wakep_fd_ready(int fd UNUSED, int events UNUSED, void *data) {
((EventLoopData*)(data))->wakeup_fd_ready = true;
}
static void
mark_key_repeat_fd_ready(int fd UNUSED, int events UNUSED, void *data) {
((EventLoopData*)(data))->key_repeat_fd_ready = true;
}
bool
initPollData(EventLoopData *eld, int display_fd) {
if (!addWatch(eld, "display", display_fd, POLLIN, 1, NULL, NULL)) return false;
@@ -252,20 +246,6 @@ initPollData(EventLoopData *eld, int display_fd) {
const int wakeup_fd = eld->wakeupFds[0];
#endif
if (!addWatch(eld, "wakeup", wakeup_fd, POLLIN, 1, mark_wakep_fd_ready, eld)) return false;
#ifdef HAS_TIMER_FD
eld->key_repeat_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
if (eld->key_repeat_fd < 0) return false;
const int key_repeat_fd = eld->key_repeat_fd;
#else
if (pipe2(eld->key_repeat_fds, O_CLOEXEC | O_NONBLOCK) != 0) return false;
const int key_repeat_fd = eld->key_repeat_fds[0];
#endif
#ifdef _GLFW_WAYLAND
if (!addWatch(eld, "key_repeat", key_repeat_fd, POLLIN, 1, mark_key_repeat_fd_ready, eld)) return false;
#else
(void)key_repeat_fd; (void)mark_key_repeat_fd_ready;
#endif
return true;
}
@@ -289,6 +269,7 @@ wakeupEventLoop(EventLoopData *eld) {
#endif
}
#ifndef HAS_EVENT_FD
static void
closeFds(int *fds, size_t count) {
while(count--) {
@@ -299,20 +280,15 @@ closeFds(int *fds, size_t count) {
fds++;
}
}
#endif
void
finalizePollData(EventLoopData *eld) {
(void)closeFds;
#ifdef HAS_EVENT_FD
close(eld->wakeupFd); eld->wakeupFd = -1;
#else
closeFds(eld->wakeupFds, arraysz(eld->wakeupFds));
#endif
#ifdef HAS_TIMER_FD
close(eld->key_repeat_fd); eld->key_repeat_fd = -1;
#else
closeFds(eld->key_repeat_fds, arraysz(eld->key_repeat_fds));
#endif
}
int
@@ -322,7 +298,7 @@ pollForEvents(EventLoopData *eld, monotonic_t timeout, watch_callback_func displ
EVDBG("pollForEvents final timeout: %.3f", monotonic_t_to_s_double(timeout));
int result;
monotonic_t end_time = monotonic() + timeout;
eld->wakeup_fd_ready = false; eld->key_repeat_fd_ready = false;
eld->wakeup_fd_ready = false;
while(1) {
if (timeout >= 0) {

16
glfw/backend_utils.h vendored
View File

@@ -36,10 +36,6 @@
#define HAS_EVENT_FD
#include <sys/eventfd.h>
#endif
#if __has_include(<sys/timerfd.h>)
#define HAS_TIMER_FD
#include <sys/timerfd.h>
#endif
#else
#define HAS_EVENT_FD
#include <sys/eventfd.h>
@@ -69,23 +65,17 @@ typedef struct {
bool repeats;
} Timer;
#define MAX_NUM_OF_WATCHED_FDS 64
typedef struct {
struct pollfd fds[MAX_NUM_OF_WATCHED_FDS];
struct pollfd fds[32];
#ifdef HAS_EVENT_FD
int wakeupFd;
#else
int wakeupFds[2];
#endif
#ifdef HAS_TIMER_FD
int key_repeat_fd;
#else
int key_repeat_fds[2];
#endif
bool wakeup_data_read, wakeup_fd_ready, key_repeat_fd_ready;
bool wakeup_data_read, wakeup_fd_ready;
nfds_t watches_count, timers_count;
Watch watches[MAX_NUM_OF_WATCHED_FDS];
Watch watches[32];
Timer timers[128];
} EventLoopData;

View File

@@ -10,36 +10,21 @@
#include "internal.h"
#include <CoreVideo/CVDisplayLink.h>
#include <os/lock.h>
#define DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL s_to_monotonic_t(30ll)
#define MAX_NUM_OF_DISPLAYS 256
typedef struct _GLFWDisplayLinkNS
{
CVDisplayLinkRef displayLink;
CGDirectDisplayID displayID;
monotonic_t lastRenderFrameRequestedAt, first_unserviced_render_frame_request_at;
bool pending_dispatch;
} _GLFWDisplayLinkNS;
static struct {
_GLFWDisplayLinkNS entries[MAX_NUM_OF_DISPLAYS];
os_unfair_lock locks[MAX_NUM_OF_DISPLAYS];
bool locks_initialized[MAX_NUM_OF_DISPLAYS];
_GLFWDisplayLinkNS entries[256];
size_t count;
} displayLinks = {0};
static inline size_t
index_for_entry(_GLFWDisplayLinkNS *entry) {
return (size_t)(entry - displayLinks.entries);
}
static inline os_unfair_lock*
lock_for_entry(_GLFWDisplayLinkNS *entry) {
return &displayLinks.locks[index_for_entry(entry)];
}
static CGDirectDisplayID
displayIDForWindow(_GLFWwindow *w) {
NSWindow *nw = w->ns.object;
@@ -52,78 +37,48 @@ displayIDForWindow(_GLFWwindow *w) {
void
_glfwClearDisplayLinks(void) {
for (size_t i = 0; i < displayLinks.count; i++) {
_GLFWDisplayLinkNS *entry = &displayLinks.entries[i];
os_unfair_lock *lock = &displayLinks.locks[i];
os_unfair_lock_lock(lock);
CVDisplayLinkRef link = entry->displayLink;
entry->displayLink = NULL;
entry->displayID = (CGDirectDisplayID)0;
entry->lastRenderFrameRequestedAt = 0;
entry->first_unserviced_render_frame_request_at = 0;
entry->pending_dispatch = false;
os_unfair_lock_unlock(lock);
if (link) {
CVDisplayLinkStop(link);
CVDisplayLinkRelease(link);
if (displayLinks.entries[i].displayLink) {
CVDisplayLinkStop(displayLinks.entries[i].displayLink);
CVDisplayLinkRelease(displayLinks.entries[i].displayLink);
}
}
memset(displayLinks.entries, 0, sizeof(_GLFWDisplayLinkNS) * displayLinks.count);
displayLinks.count = 0;
}
static void _glfwDispatchRenderFrame(void *);
static CVReturn
displayLinkCallback(
CVDisplayLinkRef displayLink UNUSED,
const CVTimeStamp* now UNUSED, const CVTimeStamp* outputTime UNUSED,
CVOptionFlags flagsIn UNUSED, CVOptionFlags* flagsOut UNUSED, void* userInfo) {
_GLFWDisplayLinkNS *entry = (_GLFWDisplayLinkNS *)userInfo;
if (entry) {
os_unfair_lock *lock = lock_for_entry(entry);
os_unfair_lock_lock(lock);
const bool should_dispatch = entry->first_unserviced_render_frame_request_at &&
!entry->pending_dispatch;
CGDirectDisplayID displayID = entry->displayID;
if (should_dispatch) entry->pending_dispatch = true;
os_unfair_lock_unlock(lock);
if (should_dispatch) dispatch_async_f(dispatch_get_main_queue(), (void*)(uintptr_t)displayID, _glfwDispatchRenderFrame);
}
CGDirectDisplayID displayID = (uintptr_t)userInfo;
NSNumber *arg = [NSNumber numberWithUnsignedInt:displayID];
[NSApp performSelectorOnMainThread:@selector(render_frame_received:) withObject:arg waitUntilDone:NO];
[arg release];
return kCVReturnSuccess;
}
static void
_glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry) {
CVDisplayLinkCreateWithCGDisplay(entry->displayID, &entry->displayLink);
if (entry->displayLink) CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, entry);
CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, (void*)(uintptr_t)entry->displayID);
}
unsigned
_glfwCreateDisplayLink(CGDirectDisplayID displayID) {
for (unsigned i = 0; i < displayLinks.count; i++) {
os_unfair_lock *existing_lock = &displayLinks.locks[i];
os_unfair_lock_lock(existing_lock);
const bool already_created = displayLinks.entries[i].displayID == displayID;
os_unfair_lock_unlock(existing_lock);
if (already_created) return i;
}
if (displayLinks.count >= MAX_NUM_OF_DISPLAYS) {
if (displayLinks.count >= arraysz(displayLinks.entries) - 1) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Too many monitors cannot create display link");
return displayLinks.count;
}
unsigned idx = displayLinks.count;
_GLFWDisplayLinkNS *entry = &displayLinks.entries[idx];
if (!displayLinks.locks_initialized[idx]) {
displayLinks.locks[idx] = OS_UNFAIR_LOCK_INIT;
displayLinks.locks_initialized[idx] = true;
for (unsigned i = 0; i < displayLinks.count; i++) {
// already created in this run
if (displayLinks.entries[i].displayID == displayID) return i;
}
os_unfair_lock *lock = &displayLinks.locks[idx];
os_unfair_lock_lock(lock);
memset(entry, 0, sizeof(entry[0]));
_GLFWDisplayLinkNS *entry = &displayLinks.entries[displayLinks.count++];
memset(entry, 0, sizeof(_GLFWDisplayLinkNS));
entry->displayID = displayID;
displayLinks.count++;
_glfw_create_cv_display_link(entry);
os_unfair_lock_unlock(lock);
return idx;
return displayLinks.count - 1;
}
static unsigned long long display_link_shutdown_timer = 0;
@@ -133,18 +88,9 @@ _glfwShutdownCVDisplayLink(unsigned long long timer_id UNUSED, void *user_data U
display_link_shutdown_timer = 0;
for (size_t i = 0; i < displayLinks.count; i++) {
_GLFWDisplayLinkNS *dl = &displayLinks.entries[i];
os_unfair_lock *lock = &displayLinks.locks[i];
os_unfair_lock_lock(lock);
CVDisplayLinkRef link = dl->displayLink;
if (link) CVDisplayLinkRetain(link);
if (dl->displayLink) CVDisplayLinkStop(dl->displayLink);
dl->lastRenderFrameRequestedAt = 0;
dl->first_unserviced_render_frame_request_at = 0;
dl->pending_dispatch = false;
os_unfair_lock_unlock(lock);
if (link) {
CVDisplayLinkStop(link);
CVDisplayLinkRelease(link);
}
}
}
@@ -161,89 +107,40 @@ _glfwRequestRenderFrame(_GLFWwindow *w) {
_GLFWDisplayLinkNS *dl = NULL;
for (size_t i = 0; i < displayLinks.count; i++) {
dl = &displayLinks.entries[i];
bool need_start = false, need_stop = false, need_recreate = false;
bool retain_link = false;
CVDisplayLinkRef link = NULL;
CVDisplayLinkRef new_link = NULL;
bool new_link_retained = false;
os_unfair_lock *lock = &displayLinks.locks[i];
os_unfair_lock_lock(lock);
link = dl->displayLink;
if (dl->displayID == displayID) {
found_display_link = true;
monotonic_t first_unserviced = dl->first_unserviced_render_frame_request_at;
dl->lastRenderFrameRequestedAt = now;
if (!first_unserviced) {
if (!dl->first_unserviced_render_frame_request_at) dl->first_unserviced_render_frame_request_at = now;
if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink);
else if (now - dl->first_unserviced_render_frame_request_at > s_to_monotonic_t(1ll)) {
// display link is stuck need to recreate it because Apple can't even
// get a simple timer right
CVDisplayLinkRelease(dl->displayLink); dl->displayLink = nil;
dl->first_unserviced_render_frame_request_at = now;
first_unserviced = now;
_glfw_create_cv_display_link(dl);
_glfwInputError(GLFW_PLATFORM_ERROR,
"CVDisplayLink stuck possibly because of sleep/screensaver + Apple's incompetence, recreating.");
if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink);
}
dl->pending_dispatch = false;
if (link) {
if (!CVDisplayLinkIsRunning(link)) {
need_start = true;
retain_link = true;
} else if (now - first_unserviced > s_to_monotonic_t(1ll)) {
need_recreate = true;
dl->first_unserviced_render_frame_request_at = now;
dl->displayLink = NULL;
}
}
} else if (link && dl->lastRenderFrameRequestedAt && now - dl->lastRenderFrameRequestedAt >= DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL) {
need_stop = true;
retain_link = true;
} else if (dl->displayLink && dl->lastRenderFrameRequestedAt && now - dl->lastRenderFrameRequestedAt >= DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL) {
CVDisplayLinkStop(dl->displayLink);
dl->lastRenderFrameRequestedAt = 0;
dl->first_unserviced_render_frame_request_at = 0;
dl->pending_dispatch = false;
}
if (retain_link && link) CVDisplayLinkRetain(link);
os_unfair_lock_unlock(lock);
if (need_recreate && link) {
CVDisplayLinkStop(link);
CVDisplayLinkRelease(link);
os_unfair_lock_lock(lock);
_glfw_create_cv_display_link(dl);
new_link = dl->displayLink;
if (new_link) {
CVDisplayLinkRetain(new_link);
new_link_retained = true;
}
dl->pending_dispatch = false;
os_unfair_lock_unlock(lock);
if (new_link) {
if (!CVDisplayLinkIsRunning(new_link)) CVDisplayLinkStart(new_link);
if (new_link_retained) CVDisplayLinkRelease(new_link);
}
_glfwInputError(GLFW_PLATFORM_ERROR,
"CVDisplayLink stuck possibly because of sleep/screensaver + Apple's incompetence, recreating.");
} else {
if (need_start && link) CVDisplayLinkStart(link);
else if (need_stop && link) CVDisplayLinkStop(link);
if (retain_link && link) CVDisplayLinkRelease(link);
}
}
if (!found_display_link) {
unsigned idx = _glfwCreateDisplayLink(displayID);
if (idx < displayLinks.count) {
dl = &displayLinks.entries[idx];
os_unfair_lock *lock = &displayLinks.locks[idx];
os_unfair_lock_lock(lock);
dl->lastRenderFrameRequestedAt = now;
dl->first_unserviced_render_frame_request_at = now;
dl->pending_dispatch = false;
CVDisplayLinkRef link = dl->displayLink;
if (link) CVDisplayLinkRetain(link);
os_unfair_lock_unlock(lock);
if (link) {
if (!CVDisplayLinkIsRunning(link)) CVDisplayLinkStart(link);
CVDisplayLinkRelease(link);
}
if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink);
}
}
}
static void
_glfwDispatchRenderFrame(void *passed_in_data) {
CGDirectDisplayID displayID = (uintptr_t)passed_in_data;
void
_glfwDispatchRenderFrame(CGDirectDisplayID displayID) {
_GLFWwindow *w = _glfw.windowListHead;
while (w) {
if (w->ns.renderFrameRequested && displayID == displayIDForWindow(w)) {
@@ -254,33 +151,10 @@ _glfwDispatchRenderFrame(void *passed_in_data) {
}
for (size_t i = 0; i < displayLinks.count; i++) {
_GLFWDisplayLinkNS *dl = &displayLinks.entries[i];
bool need_stop = false;
CVDisplayLinkRef link = NULL;
os_unfair_lock *lock = &displayLinks.locks[i];
os_unfair_lock_lock(lock);
if (dl->displayID == displayID) {
dl->first_unserviced_render_frame_request_at = 0;
dl->pending_dispatch = false;
bool any_pending_request = false;
_GLFWwindow *window = _glfw.windowListHead;
while (window) {
if (window->ns.renderFrameRequested && displayID == displayIDForWindow(window)) {
any_pending_request = true;
break;
}
window = window->next;
}
link = dl->displayLink;
if (!any_pending_request && link && CVDisplayLinkIsRunning(link)) {
need_stop = true;
CVDisplayLinkRetain(link);
dl->lastRenderFrameRequestedAt = 0;
}
}
os_unfair_lock_unlock(lock);
if (need_stop && link) {
CVDisplayLinkStop(link);
CVDisplayLinkRelease(link);
}
}
}

View File

@@ -29,7 +29,6 @@
#include "internal.h"
#include "../kitty/monotonic.h"
#include <sys/param.h> // For MAXPATHLEN
#include <sys/sysctl.h>
#include <pthread.h>
// Needed for _NSGetProgname
@@ -298,37 +297,10 @@ static NSDictionary<NSString*,NSNumber*> *global_shortcuts = nil;
// Delegate for application related notifications {{{
@interface GLFWApplicationDelegate : NSObject <NSApplicationDelegate>
- (void)handleAppearanceChange;
@end
@implementation GLFWApplicationDelegate
- (void)applicationDidActivate:(NSNotification *)notification {
NSRunningApplication *app = notification.userInfo[NSWorkspaceApplicationKey];
if (app && app.processIdentifier != getpid()) {
_glfw.ns.previous_front_most_application = app.processIdentifier;
debug_rendering("Front most application changed to: %s pid: %d\n", app.bundleIdentifier.UTF8String, app.processIdentifier)
}
}
- (void)applicationDidBecomeActive:(NSNotification *)notification {
(void)notification;
// When the application becomes active after switching spaces (e.g., swiping
// back from a fullscreen app on another space), macOS may not send
// windowDidBecomeKey: for the already-key window. This leaves GLFW thinking
// no window has focus (since windowDidResignKey: was sent when leaving).
// Ensure GLFW's focus state is updated to match the actual key window.
NSWindow *keyWindow = [NSApp keyWindow];
if (keyWindow && !_glfw.focusedWindowId) {
for (_GLFWwindow *window = _glfw.windowListHead; window; window = window->next) {
if (window->ns.object == keyWindow) {
if (_glfw.focusedWindowId != window->id) _glfwInputWindowFocus(window, true);
break;
}
}
}
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
(void)sender;
@@ -435,76 +407,20 @@ static GLFWapplicationwillfinishlaunchingfun finish_launching_callback = NULL;
}
}
static void *AppearanceObservationContext = &AppearanceObservationContext;
static NSDate *application_finished_launching_at = nil;
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
(void)notification;
[[NSApplication sharedApplication] addObserver:self
forKeyPath:@"effectiveAppearance" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial
context:AppearanceObservationContext];
if (finish_launching_callback) finish_launching_callback(true);
(void)notification;
[NSApp stop:nil];
CGDisplayRegisterReconfigurationCallback(display_reconfigured, NULL);
_glfwCocoaPostEmptyEvent();
application_finished_launching_at = [NSDate date];
}
GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialized) {
(void)query_if_unintialized;
int theme_type = GLFW_COLOR_SCHEME_NO_PREFERENCE;
NSAppearance *changedAppearance = NSApp.effectiveAppearance;
NSAppearanceName newAppearance = [changedAppearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];
if([newAppearance isEqualToString:NSAppearanceNameDarkAqua]){
theme_type = GLFW_COLOR_SCHEME_DARK;
} else {
theme_type = GLFW_COLOR_SCHEME_LIGHT;
}
return theme_type;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey, id> *)change
context:(void *)context {
if (context == AppearanceObservationContext) {
if ([keyPath isEqualToString:@"effectiveAppearance"]) {
// The initial call (from NSKeyValueObservingOptionInitial) might happen on a background thread.
// Dispatch to the main thread to be safe, especially if updating UI.
__block __typeof__(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf handleAppearanceChange];
});
}
} else {
// If the context doesn't match, pass the notification to the superclass.
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)handleAppearanceChange {
static GLFWColorScheme previously_reported_appearance = GLFW_COLOR_SCHEME_NO_PREFERENCE;
GLFWColorScheme new_appearance = glfwGetCurrentSystemColorTheme(true);
if (new_appearance != previously_reported_appearance) {
previously_reported_appearance = new_appearance;
_glfwInputColorScheme(new_appearance, false);
}
}
- (void)applicationWillTerminate:(NSNotification *)aNotification
{
(void)aNotification;
CGDisplayRemoveReconfigurationCallback(display_reconfigured, NULL);
@try {
[[NSApplication sharedApplication] removeObserver:self
forKeyPath:@"effectiveAppearance"
context:AppearanceObservationContext];
} @catch (NSException * __unused exception) {
// Ignore exceptions, which can happen if the observer was never added.
}
}
- (void)applicationDidHide:(NSNotification *)notification
@@ -522,6 +438,7 @@ GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialize
@interface GLFWApplication : NSApplication
- (void)tick_callback;
- (void)render_frame_received:(id)displayIDAsID;
@end
@implementation GLFWApplication
@@ -530,6 +447,11 @@ GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialize
_glfwDispatchTickCallback();
}
- (void)render_frame_received:(id)displayIDAsID
{
CGDirectDisplayID displayID = [(NSNumber*)displayIDAsID unsignedIntValue];
_glfwDispatchRenderFrame(displayID);
}
@end
@@ -857,24 +779,6 @@ is_apple_jis_layout_function_key(NSEvent *event) {
return [event keyCode] == 0x66 /* kVK_JIS_Eisu */ || [event keyCode] == 0x68 /* kVK_JIS_Kana */;
}
static bool
has_apple_fn_global_shortcut(void) {
NSDictionary *hitoolbox_settings = [[NSUserDefaults standardUserDefaults] persistentDomainForName:@"com.apple.HIToolbox"];
id obj = [hitoolbox_settings objectForKey:@"AppleFnUsageType"];
if (![obj isKindOfClass:[NSNumber class]]) return false;
// Non-zero AppleFnUsageType means macOS has reserved Fn/Globe for a
// system action such as input source switching, emoji picker, or dictation.
return [obj integerValue] != 0;
}
static bool
is_apple_fn_global_shortcut(NSEvent *event) {
if ([event keyCode] != 0x3f /* kVK_Function */) return false;
NSEventModifierFlags mods = USEFUL_MODS([event modifierFlags]);
if (mods != 0 && mods != NSEventModifierFlagFunction) return false;
return has_apple_fn_global_shortcut();
}
GLFWAPI GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback) {
GLFWapplicationshouldhandlereopenfun previous = handle_reopen_callback;
handle_reopen_callback = callback;
@@ -912,11 +816,6 @@ int _glfwPlatformInit(bool *supports_window_occlusion)
}
[NSApp setDelegate:_glfw.ns.delegate];
[[[NSWorkspace sharedWorkspace] notificationCenter]
addObserver:_glfw.ns.delegate
selector:@selector(applicationDidActivate:)
name:NSWorkspaceDidActivateApplicationNotification
object:nil];
static struct {
unsigned short virtual_key_code;
NSEventModifierFlags input_source_switch_modifiers;
@@ -927,12 +826,12 @@ int _glfwPlatformInit(bool *supports_window_occlusion)
NSEvent* (^keydown_block)(NSEvent*) = ^ NSEvent* (NSEvent* event)
{
debug_input("---------------- key down -------------------\n");
debug_input("%s\n", [[event description] UTF8String]);
if (!_glfw.ignoreOSKeyboardProcessing && !_glfw.keyboard_grabbed) {
debug_key("---------------- key down -------------------\n");
debug_key("%s\n", [[event description] UTF8String]);
if (!_glfw.ignoreOSKeyboardProcessing) {
// first check if there is a global menu bar shortcut
if ([[NSApp mainMenu] performKeyEquivalent:event]) {
debug_input("keyDown triggered global menu bar action ignoring\n");
debug_key("keyDown triggered global menu bar action ignoring\n");
last_keydown_shortcut_event.virtual_key_code = [event keyCode];
last_keydown_shortcut_event.input_source_switch_modifiers = 0;
last_keydown_shortcut_event.timestamp = [event timestamp];
@@ -941,7 +840,7 @@ int _glfwPlatformInit(bool *supports_window_occlusion)
// now check if there is a useful apple shortcut
int global_shortcut = is_active_apple_global_shortcut(event);
if (is_useful_apple_global_shortcut(global_shortcut)) {
debug_input("keyDown triggered global macOS shortcut ignoring\n");
debug_key("keyDown triggered global macOS shortcut ignoring\n");
last_keydown_shortcut_event.virtual_key_code = [event keyCode];
// record the modifier keys if switching to the next input source
last_keydown_shortcut_event.input_source_switch_modifiers = (global_shortcut == kSHKSelectNextSourceInInputMenu) ? USEFUL_MODS([event modifierFlags]) : 0;
@@ -950,7 +849,7 @@ int _glfwPlatformInit(bool *supports_window_occlusion)
}
// check for JIS keyboard layout function keys
if (is_apple_jis_layout_function_key(event)) {
debug_input("keyDown triggered JIS layout function key ignoring\n");
debug_key("keyDown triggered JIS layout function key ignoring\n");
last_keydown_shortcut_event.virtual_key_code = [event keyCode];
last_keydown_shortcut_event.input_source_switch_modifiers = 0;
last_keydown_shortcut_event.timestamp = [event timestamp];
@@ -960,35 +859,31 @@ int _glfwPlatformInit(bool *supports_window_occlusion)
last_keydown_shortcut_event.virtual_key_code = 0xffff;
NSWindow *kw = [NSApp keyWindow];
if (kw && kw.contentView) [kw.contentView keyDown:event];
else debug_input("keyDown ignored as no keyWindow present\n");
else debug_key("keyDown ignored as no keyWindow present\n");
return nil;
};
NSEvent* (^keyup_block)(NSEvent*) = ^ NSEvent* (NSEvent* event)
{
debug_input("----------------- key up --------------------\n");
debug_input("%s\n", [[event description] UTF8String]);
debug_key("----------------- key up --------------------\n");
debug_key("%s\n", [[event description] UTF8String]);
if (last_keydown_shortcut_event.virtual_key_code != 0xffff && last_keydown_shortcut_event.virtual_key_code == [event keyCode]) {
// ignore as the corresponding key down event triggered a menu bar or macOS shortcut
last_keydown_shortcut_event.virtual_key_code = 0xffff;
debug_input("keyUp ignored as corresponds to previous keyDown that triggered a shortcut\n");
debug_key("keyUp ignored as corresponds to previous keyDown that triggered a shortcut\n");
return nil;
}
NSWindow *kw = [NSApp keyWindow];
if (kw && kw.contentView) [kw.contentView keyUp:event];
else debug_input("keyUp ignored as no keyWindow present\n");
else debug_key("keyUp ignored as no keyWindow present\n");
return nil;
};
NSEvent* (^flags_changed_block)(NSEvent*) = ^ NSEvent* (NSEvent* event)
{
debug_input("-------------- flags changed -----------------\n");
debug_input("%s\n", [[event description] UTF8String]);
debug_key("-------------- flags changed -----------------\n");
debug_key("%s\n", [[event description] UTF8String]);
last_keydown_shortcut_event.virtual_key_code = 0xffff;
if (!_glfw.ignoreOSKeyboardProcessing && !_glfw.keyboard_grabbed && is_apple_fn_global_shortcut(event)) {
debug_input("flagsChanged triggered global fn shortcut ignoring\n");
return event;
}
// switching to the next input source is only confirmed when all modifier keys are released
if (last_keydown_shortcut_event.input_source_switch_modifiers) {
if (!([event modifierFlags] & last_keydown_shortcut_event.input_source_switch_modifiers))
@@ -997,7 +892,7 @@ int _glfwPlatformInit(bool *supports_window_occlusion)
}
NSWindow *kw = [NSApp keyWindow];
if (kw && kw.contentView) [kw.contentView flagsChanged:event];
else debug_input("flagsChanged ignored as no keyWindow present\n");
else debug_key("flagsChanged ignored as no keyWindow present\n");
return nil;
};
@@ -1014,16 +909,13 @@ int _glfwPlatformInit(bool *supports_window_occlusion)
if (_glfw.hints.init.ns.chdir)
changeToResourcesDirectory();
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
NSDictionary* defaults = @{
// Press and Hold prevents some keys from emitting repeated characters
@"ApplePressAndHoldEnabled": @NO,
// Dont generate openFile events from command line arguments
@"NSTreatUnknownArgumentsAsOpen": @"NO",
// This Tahoe nonsense causes slowdowns in some situations, see for example:
// https://issues.chromium.org/issues/452372350 it doesnt affect
// autofill via Edit->Autofill
@"NSAutoFillHeuristicControllerEnabled" : @NO,
}];
};
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
NSUserDefaults *apple_settings = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.symbolichotkeys"];
[apple_settings addObserver:_glfw.ns.helper
@@ -1052,47 +944,11 @@ int _glfwPlatformInit(bool *supports_window_occlusion)
} // autoreleasepool
}
static NSDate*
get_process_start_time(pid_t pid) {
struct kinfo_proc kp;
size_t len = sizeof(kp);
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid };
if (sysctl(mib, 4, &kp, &len, NULL, 0) == 0 && len == sizeof(kp)) {
struct timeval start_tv = kp.kp_proc.p_starttime;
time_t start_sec = start_tv.tv_sec;
suseconds_t start_usec = start_tv.tv_usec;
NSTimeInterval t = (NSTimeInterval)start_sec + ((NSTimeInterval)start_usec / 1000000.0);
return [NSDate dateWithTimeIntervalSince1970:t];
}
return nil;
}
void _glfwPlatformTerminate(void)
{
@autoreleasepool {
// Kill the AutoFill helper process that macOS Tahoe starts and fails to
// shutdown on application exit, see https://github.com/kovidgoyal/kitty/issues/9299
// Only kill helpers that were launched within a few seconds of this process to
// avoid killing helpers from other processes. This is obviously not robust
// but since Apple cant design its way out of a paper bag, it's the best we
// can do.
if (application_finished_launching_at != nil) {
for (NSRunningApplication *app in [[NSWorkspace sharedWorkspace] runningApplications]) {
if ([app.bundleIdentifier isEqualToString:@"com.apple.SafariPlatformSupport.Helper"] &&
[[app.localizedName lowercaseString] containsString:@"autofill (kitty)"]) {
NSDate *st = get_process_start_time(app.processIdentifier);
if (st != nil) {
NSTimeInterval timeDifference = [application_finished_launching_at timeIntervalSinceDate:st];
[st release];
if (fabs(timeDifference) <= 5) [app forceTerminate];
}
}
}
[application_finished_launching_at release]; application_finished_launching_at = nil;
}
_glfwClearDisplayLinks();
if (_glfw.ns.inputSource)
@@ -1288,4 +1144,3 @@ void _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval,
}
void _glfwPlatformInputColorScheme(GLFWColorScheme appearance UNUSED) { }
bool _glfwPlatformGrabKeyboard(bool grab UNUSED) { return true; /* directly uses _glfw.keyboard_grabbed */ }

View File

@@ -32,7 +32,6 @@
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <assert.h>
#include <IOKit/graphics/IOGraphicsLib.h>
#include <CoreVideo/CVBase.h>
@@ -636,8 +635,6 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle)
{
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
assert(monitor != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay);
return monitor->ns.displayID;
}

35
glfw/cocoa_platform.h vendored
View File

@@ -62,6 +62,8 @@ typedef void* id;
#define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity
#endif
#define debug_key(...) if (_glfw.hints.init.debugKeyboard) { fprintf(stderr, __VA_ARGS__); fflush(stderr); }
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int, unsigned long);
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
typedef bool (* GLFWhandleurlopen)(const char*);
@@ -114,21 +116,6 @@ typedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef);
typedef UInt8 (*PFN_LMGetKbdType)(void);
#define LMGetKbdType _glfw.ns.tis.GetKbdType
typedef struct _GLFWDropData {
const char **mimes; // Original MIME list; strings are owned here, never reordered
size_t mimes_count;
const char **copy_mimes; // Working copy passed to callbacks; pointers into mimes[]
size_t copy_mimes_count; // Accepted count after last callback
bool drag_accepted;
struct {
unsigned long long request_id;
id temp_dir;
id data_map; // map MIME to NSError or NSInputStream
id path_map; // map MIME to NSError or NSInputStream or NSUrl
id pending_requests;
bool promises_loaded;
} in_progress_drop;
} _GLFWDropData;
// Cocoa-specific per-window data
//
@@ -143,23 +130,14 @@ typedef struct _GLFWwindowNS
bool retina;
bool in_traditional_fullscreen;
bool in_fullscreen_transition;
bool suppress_frame_constraints;
id notch_cover_window;
unsigned int notch_cover_color;
float notch_cover_opacity;
bool titlebar_hidden;
unsigned long pre_full_screen_style_mask;
CGRect pre_traditional_fullscreen_frame;
// Cached window properties to filter out duplicate events
int width, height;
int fbWidth, fbHeight;
float xscale, yscale;
int blur_radius;
bool live_resize_in_progress;
struct {
struct { CGFloat red, green, blue, alpha; bool was_set; } color; bool transparent;
} last_applied_titlebar_settings;
// The total sum of the distances the cursor has been warped
// since the last cursor motion event was processed
@@ -185,11 +163,6 @@ typedef struct _GLFWwindowNS
// update cursor after switching desktops with Mission Control
bool delayed_cursor_update_requested;
GLFWcocoarenderframefun resizeCallback;
// Cached MIME types from drag enter (for move events)
_GLFWDropData drop_data;
unsigned long long drop_request_counter;
} _GLFWwindowNS;
// Cocoa-specific global data
@@ -215,7 +188,6 @@ typedef struct _GLFWlibraryNS
double restoreCursorPosX, restoreCursorPosY;
// The window whose disabled cursor mode is active
_GLFWwindow* disabledCursorWindow;
pid_t previous_front_most_application;
struct {
CFBundleRef bundle;
@@ -228,8 +200,6 @@ typedef struct _GLFWlibraryNS
// the callback to handle url open events
GLFWhandleurlopen url_open_callback;
// Active drag session (NSDraggingSession*) and view (NSView*)
id drag_session, drag_view, drag_image;
} _GLFWlibraryNS;
// Cocoa-specific per-monitor data
@@ -273,6 +243,7 @@ void* _glfwLoadLocalVulkanLoaderNS(void);
void _glfwClearDisplayLinks(void);
void _glfwRestartDisplayLinks(void);
unsigned _glfwCreateDisplayLink(CGDirectDisplayID);
void _glfwDispatchRenderFrame(CGDirectDisplayID);
void _glfwRequestRenderFrame(_GLFWwindow *w);
// event loop

File diff suppressed because it is too large Load Diff

32
glfw/egl_context.c vendored
View File

@@ -223,7 +223,6 @@ static int extensionSupportedEGL(const char* extension)
static GLFWglproc getProcAddressEGL(const char* procname)
{
_GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
assert(window != NULL);
if (window->context.egl.client)
{
@@ -388,7 +387,6 @@ bool _glfwInitEGL(void)
}
_glfw.egl.platform = _glfwPlatformGetEGLPlatform(&attribs);
_glfw.egl.display = EGL_NO_DISPLAY;
if (_glfw.egl.platform)
{
_glfw.egl.display =
@@ -396,18 +394,16 @@ bool _glfwInitEGL(void)
_glfwPlatformGetEGLNativeDisplay(),
attribs);
}
else
_glfw.egl.display = eglGetDisplay(_glfwPlatformGetEGLNativeDisplay());
free(attribs);
if (_glfw.egl.display == EGL_NO_DISPLAY)
_glfw.egl.display = eglGetDisplay(_glfwPlatformGetEGLNativeDisplay());
EGLint egl_err;
if (_glfw.egl.display == EGL_NO_DISPLAY && (egl_err = eglGetError()) != EGL_SUCCESS)
{
_glfwInputError(GLFW_API_UNAVAILABLE,
"EGL: Failed to get EGL display: %s",
getEGLErrorString(egl_err));
getEGLErrorString(eglGetError()));
_glfwTerminateEGL();
return false;
@@ -416,8 +412,8 @@ bool _glfwInitEGL(void)
if (!eglInitialize(_glfw.egl.display, &_glfw.egl.major, &_glfw.egl.minor))
{
_glfwInputError(GLFW_API_UNAVAILABLE,
"EGL: Failed to initialize EGL: %s display_present: %d egl_platform_present: %d",
getEGLErrorString(eglGetError()), _glfw.egl.display != EGL_NO_DISPLAY, _glfw.egl.platform != 0);
"EGL: Failed to initialize EGL: %s",
getEGLErrorString(eglGetError()));
_glfwTerminateEGL();
return false;
@@ -546,18 +542,18 @@ bool _glfwCreateContextEGL(_GLFWwindow* window,
flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;
}
if (ctxconfig->major != 1 || ctxconfig->minor != 0)
{
setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major);
setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor);
}
if (ctxconfig->noerror)
{
if (_glfw.egl.KHR_create_context_no_error)
setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, true);
}
if (ctxconfig->major != 1 || ctxconfig->minor != 0)
{
setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major);
setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor);
}
if (mask)
setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask);
@@ -793,8 +789,6 @@ GLFWAPI EGLDisplay glfwGetEGLDisplay(void)
GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT);
if (window->context.client == GLFW_NO_API)
@@ -809,8 +803,6 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle)
GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE);
if (window->context.client == GLFW_NO_API)

View File

@@ -2,13 +2,234 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
import json
import os
import re
import subprocess
import sys
from enum import Enum
from typing import Any, Callable, Dict, List, NamedTuple, Optional, Sequence, Tuple
_plat = sys.platform.lower()
is_linux = 'linux' in _plat
is_openbsd = 'openbsd' in _plat
base = os.path.dirname(os.path.abspath(__file__))
def null_func() -> None:
return None
class CompileKey(NamedTuple):
src: str
dest: str
class Command(NamedTuple):
desc: str
cmd: Sequence[str]
is_newer_func: Callable[[], bool]
on_success: Callable[[], None] = null_func
key: Optional[CompileKey] = None
keyfile: Optional[str] = None
class ISA(Enum):
X86 = 0x03
AMD64 = 0x3e
ARM64 = 0xb7
Other = 0x0
class BinaryArch(NamedTuple):
bits: int = 64
isa: ISA = ISA.AMD64
class CompilerType(Enum):
gcc = 'gcc'
clang = 'clang'
unknown = 'unknown'
class Env:
cc: List[str] = []
cppflags: List[str] = []
cflags: List[str] = []
ldflags: List[str] = []
library_paths: Dict[str, List[str]] = {}
ldpaths: List[str] = []
ccver: Tuple[int, int]
vcs_rev: str = ''
binary_arch: BinaryArch = BinaryArch()
native_optimizations: bool = False
primary_version: int = 0
secondary_version: int = 0
xt_version: str = ''
has_copy_file_range: bool = False
# glfw stuff
all_headers: List[str] = []
sources: List[str] = []
wayland_packagedir: str = ''
wayland_scanner: str = ''
wayland_scanner_code: str = ''
wayland_protocols: Tuple[str, ...] = ()
def __init__(
self, cc: List[str] = [], cppflags: List[str] = [], cflags: List[str] = [], ldflags: List[str] = [],
library_paths: Dict[str, List[str]] = {}, ldpaths: Optional[List[str]] = None, ccver: Tuple[int, int] = (0, 0),
vcs_rev: str = '', binary_arch: BinaryArch = BinaryArch(),
native_optimizations: bool = False,
):
self.cc, self.cppflags, self.cflags, self.ldflags, self.library_paths = cc, cppflags, cflags, ldflags, library_paths
self.ldpaths = ldpaths or []
self.ccver = ccver
self.vcs_rev = vcs_rev
self.binary_arch = binary_arch
self.native_optimizations = native_optimizations
self._cc_version_string = ''
self._compiler_type: Optional[CompilerType] = None
@property
def cc_version_string(self) -> str:
if not self._cc_version_string:
self._cc_version_string = subprocess.check_output(self.cc + ['--version']).decode()
return self._cc_version_string
@property
def compiler_type(self) -> CompilerType:
if self._compiler_type is None:
raw = self.cc_version_string
if 'Free Software Foundation' in raw:
self._compiler_type = CompilerType.gcc
elif 'clang' in raw.lower().split():
self._compiler_type = CompilerType.clang
else:
self._compiler_type = CompilerType.unknown
return self._compiler_type
def copy(self) -> 'Env':
ans = Env(self.cc, list(self.cppflags), list(self.cflags), list(self.ldflags), dict(self.library_paths), list(self.ldpaths), self.ccver)
ans.all_headers = list(self.all_headers)
ans._cc_version_string = self._cc_version_string
ans.sources = list(self.sources)
ans.wayland_packagedir = self.wayland_packagedir
ans.wayland_scanner = self.wayland_scanner
ans.wayland_scanner_code = self.wayland_scanner_code
ans.wayland_protocols = self.wayland_protocols
ans.vcs_rev = self.vcs_rev
ans.binary_arch = self.binary_arch
ans.native_optimizations = self.native_optimizations
ans.primary_version = self.primary_version
ans.secondary_version = self.secondary_version
ans.xt_version = self.xt_version
ans.has_copy_file_range = self.has_copy_file_range
return ans
def wayland_protocol_file_name(base: str, ext: str = 'c') -> str:
base = os.path.basename(base).rpartition('.')[0]
return f'wayland-{base}-client-protocol.{ext}'
def init_env(
env: Env,
pkg_config: Callable[..., List[str]],
pkg_version: Callable[[str], Tuple[int, int]],
at_least_version: Callable[..., None],
test_compile: Callable[..., Any],
module: str = 'x11'
) -> Env:
ans = env.copy()
ans.cflags.append('-fPIC')
ans.cppflags.append(f'-D_GLFW_{module.upper()}')
ans.cppflags.append('-D_GLFW_BUILD_DLL')
with open(os.path.join(base, 'source-info.json')) as f:
sinfo = json.load(f)
module_sources = list(sinfo[module]['sources'])
if module in ('x11', 'wayland'):
remove = 'null_joystick.c' if is_linux else 'linux_joystick.c'
module_sources.remove(remove)
ans.sources = sinfo['common']['sources'] + module_sources
ans.all_headers = [x for x in os.listdir(base) if x.endswith('.h')]
if module in ('x11', 'wayland'):
ans.cflags.append('-pthread')
ans.ldpaths.extend('-pthread -lm'.split())
if not is_openbsd:
ans.ldpaths.extend('-lrt -ldl'.split())
major, minor = pkg_version('xkbcommon')
if (major, minor) < (0, 5):
raise SystemExit('libxkbcommon >= 0.5 required')
if major < 1:
ans.cflags.append('-DXKB_HAS_NO_UTF32')
if module == 'x11':
for dep in 'x11 xrandr xinerama xcursor xkbcommon xkbcommon-x11 x11-xcb dbus-1'.split():
ans.cflags.extend(pkg_config(dep, '--cflags-only-I'))
ans.ldpaths.extend(pkg_config(dep, '--libs'))
elif module == 'cocoa':
ans.cppflags.append('-DGL_SILENCE_DEPRECATION')
for f_ in 'Cocoa IOKit CoreFoundation CoreVideo UniformTypeIdentifiers'.split():
ans.ldpaths.extend(('-framework', f_))
elif module == 'wayland':
at_least_version('wayland-protocols', *sinfo['wayland_protocols'])
ans.wayland_packagedir = os.path.abspath(pkg_config('wayland-protocols', '--variable=pkgdatadir')[0])
ans.wayland_scanner = os.path.abspath(pkg_config('wayland-scanner', '--variable=wayland_scanner')[0])
scanner_version = tuple(map(int, pkg_config('wayland-scanner', '--modversion')[0].strip().split('.')))
ans.wayland_scanner_code = 'private-code' if scanner_version >= (1, 14, 91) else 'code'
ans.wayland_protocols = tuple(sinfo[module]['protocols'])
for p in ans.wayland_protocols:
ans.sources.append(wayland_protocol_file_name(p))
ans.all_headers.append(wayland_protocol_file_name(p, 'h'))
for dep in 'wayland-client wayland-cursor xkbcommon dbus-1'.split():
ans.cflags.extend(pkg_config(dep, '--cflags-only-I'))
ans.ldpaths.extend(pkg_config(dep, '--libs'))
has_memfd_create = test_compile(env.cc, '-Werror', src='''#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
int main(void) {
return syscall(__NR_memfd_create, "test", 0);
}''')
if has_memfd_create:
ans.cppflags.append('-DHAS_MEMFD_CREATE')
return ans
def build_wayland_protocols(
env: Env,
parallel_run: Callable[[List[Command]], None],
emphasis: Callable[[str], str],
newer: Callable[..., bool],
dest_dir: str
) -> None:
items = []
for protocol in env.wayland_protocols:
if '/' in protocol:
src = os.path.join(env.wayland_packagedir, protocol)
if not os.path.exists(src):
raise SystemExit(f'The wayland-protocols package on your system is missing the {protocol} protocol definition file')
else:
src = os.path.join(os.path.dirname(os.path.abspath(__file__)), protocol)
if not os.path.exists(src):
raise SystemExit(f'The local Wayland protocol {protocol} is missing from kitty sources')
for ext in 'hc':
dest = wayland_protocol_file_name(src, ext)
dest = os.path.join(dest_dir, dest)
if newer(dest, src):
q = 'client-header' if ext == 'h' else env.wayland_scanner_code
items.append(Command(
f'Generating {emphasis(os.path.basename(dest))} ...',
[env.wayland_scanner, q, src, dest], lambda: True))
if items:
parallel_run(items)
class Arg:
@@ -88,15 +309,12 @@ def generate_wrappers(glfw_header: str) -> None:
GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunching(GLFWapplicationwillfinishlaunchingfun callback)
uint32_t glfwGetCocoaKeyEquivalent(uint32_t glfw_key, int glfw_mods, int* cocoa_mods)
void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun callback)
bool glfwCocoaRecreateGLDrawable(GLFWwindow *w)
GLFWcocoarenderframefun glfwCocoaSetWindowResizeCallback(GLFWwindow *w, GLFWcocoarenderframefun callback)
void* glfwGetX11Display(void)
unsigned long glfwGetX11Window(GLFWwindow* window)
void glfwSetPrimarySelectionString(GLFWwindow* window, const char* string)
void glfwCocoaCycleThroughOSWindows(bool backwards)
void glfwCocoaSetWindowChrome(GLFWwindow* window, 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)
void glfwCocoaRegisterMIMETypes(GLFWwindow *window, const char **mimes, size_t count)
const char* glfwGetPrimarySelectionString(GLFWwindow* window, void)
int glfwGetNativeKeyForName(const char* key_name, int case_sensitive)
void glfwRequestWaylandFrameEvent(GLFWwindow *handle, unsigned long long id, GLFWwaylandframecallbackfunc callback)
@@ -104,12 +322,11 @@ def generate_wrappers(glfw_header: str) -> None:
const char* glfwWaylandMissingCapabilities(void)
void glfwWaylandRunWithActivationToken(GLFWwindow *handle, GLFWactivationcallback cb, void *cb_data)
bool glfwWaylandSetTitlebarColor(GLFWwindow *handle, uint32_t color, bool use_system_color)
void glfwWaylandSetTitlebarHidden(GLFWwindow *handle, bool hidden)
void glfwWaylandRedrawCSDWindowTitle(GLFWwindow *handle)
bool glfwWaylandIsWindowFullyCreated(GLFWwindow *handle)
bool glfwWaylandBeep(GLFWwindow *handle)
GLFWLayerShellConfig* glfwWaylandLayerShellConfig(GLFWwindow *handle)
pid_t glfwWaylandCompositorPID(void)
void glfwConfigureMomentumScroller(double friction, double min_velocity, double max_velocity, unsigned timer_interval)
unsigned long long glfwDBusUserNotify(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *data)
void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler)
int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc)

226
glfw/glfw3.h vendored
View File

@@ -109,16 +109,6 @@ extern "C" {
#include <stdbool.h>
/* Include for ssize_t on POSIX systems. On Windows we define it if needed. */
#if !defined(_WIN32)
#include <sys/types.h>
#else
#if !defined(SSIZE_T_DEFINED)
typedef intptr_t ssize_t;
#define SSIZE_T_DEFINED
#endif
#endif
#if defined(GLFW_INCLUDE_VULKAN)
#include <vulkan/vulkan.h>
#endif /* Vulkan header */
@@ -556,30 +546,6 @@ typedef enum GLFWColorScheme {
GLFW_COLOR_SCHEME_LIGHT = 2
} GLFWColorScheme;
typedef enum GLFWMomentumType {
GLFW_NO_MOMENTUM_DATA = 0,
GLFW_MOMENTUM_PHASE_BEGAN = 1,
GLFW_MOMENTUM_PHASE_STATIONARY = 2,
GLFW_MOMENTUM_PHASE_ACTIVE = 3,
GLFW_MOMENTUM_PHASE_ENDED = 4,
GLFW_MOMENTUM_PHASE_CANCELED = 5,
GLFW_MOMENTUM_PHASE_MAY_BEGIN = 6,
} GLFWMomentumType;
typedef enum GLFWOffsetType {
GLFW_SCROLL_OFFSET_LINES = 0,
GLFW_SCROLL_OFFEST_V120 = 1,
GLFW_SCROLL_OFFEST_HIGHRES = 2,
} GLFWOffsetType;
typedef struct GLFWScrollEvent {
double x_offset, y_offset; // offsets are scaled by the window scale for HIGHRES
struct { double x, y; } unscaled; // unscaled offsets, aka logical pixels
GLFWMomentumType momentum_type;
GLFWOffsetType offset_type;
int keyboard_modifiers;
} GLFWScrollEvent;
/*! @defgroup joysticks Joysticks
* @brief Joystick IDs.
*
@@ -1090,7 +1056,6 @@ typedef enum {
#define GLFW_WAYLAND_APP_ID 0x00025001
#define GLFW_WAYLAND_BGCOLOR 0x00025002
#define GLFW_WAYLAND_WINDOW_TAG 0x00025003
/*! @} */
#define GLFW_NO_API 0
@@ -1277,20 +1242,6 @@ typedef struct GLFWwindow GLFWwindow;
*/
typedef struct GLFWcursor GLFWcursor;
/*! @brief Opaque drop data object.
*
* Opaque drop data object representing data from a drag and drop operation.
* This object is passed to the drop callback and can be used to query
* available MIME types and read the dropped data in chunks.
*
* @see @ref path_drop
* @see @ref glfwGetDropMimeTypes
* @see @ref glfwReadDropData
*
* @since Added in version 4.0.
*
* @ingroup input
*/
typedef enum {
GLFW_RELEASE = 0,
GLFW_PRESS = 1,
@@ -1351,19 +1302,14 @@ typedef struct GLFWkeyevent
typedef enum { GLFW_LAYER_SHELL_NONE, GLFW_LAYER_SHELL_BACKGROUND, GLFW_LAYER_SHELL_PANEL, GLFW_LAYER_SHELL_TOP, GLFW_LAYER_SHELL_OVERLAY } GLFWLayerShellType;
typedef enum { GLFW_EDGE_TOP, GLFW_EDGE_BOTTOM, GLFW_EDGE_LEFT, GLFW_EDGE_RIGHT, GLFW_EDGE_CENTER, GLFW_EDGE_NONE, GLFW_EDGE_CENTER_SIZED } GLFWEdge;
typedef enum { GLFW_EDGE_TOP, GLFW_EDGE_BOTTOM, GLFW_EDGE_LEFT, GLFW_EDGE_RIGHT, GLFW_EDGE_CENTER, GLFW_EDGE_NONE } GLFWEdge;
typedef enum { GLFW_FOCUS_NOT_ALLOWED, GLFW_FOCUS_EXCLUSIVE, GLFW_FOCUS_ON_DEMAND} GLFWFocusPolicy;
typedef struct GLFWLayerShellConfig {
GLFWLayerShellType type;
GLFWEdge edge;
struct {
GLFWEdge edge;
int requested_top_margin, requested_left_margin, requested_bottom_margin, requested_right_margin;
} previous;
bool was_toggled_to_fullscreen;
char output_name[128];
char output_name[64];
GLFWFocusPolicy focus_policy;
unsigned x_size_in_cells, x_size_in_pixels;
unsigned y_size_in_cells, y_size_in_pixels;
@@ -1382,39 +1328,6 @@ typedef struct GLFWDBUSNotificationData {
int32_t timeout; uint8_t urgency; uint32_t replaces; int muted;
} GLFWDBUSNotificationData;
typedef enum { GLFW_DROP_ENTER, GLFW_DROP_MOVE, GLFW_DROP_LEAVE, GLFW_DROP_DROP, GLFW_DROP_STATUS_UPDATE, GLFW_DROP_DATA_AVAILABLE } GLFWDropEventType;
/*! @brief Drag operation types.
*
* These constants specify the type of drag operation (copy, move, or generic).
*
* @ingroup input
*/
typedef enum {
GLFW_DRAG_OPERATION_NONE = 0, // no operation, drop was not accepted
/*! Move the dragged data to the destination. */
GLFW_DRAG_OPERATION_MOVE = 1,
/*! Copy the dragged data to the destination. */
GLFW_DRAG_OPERATION_COPY = 2,
/*! Generic drag operation (platform decides semantics). */
GLFW_DRAG_OPERATION_GENERIC = 4
} GLFWDragOperationType;
typedef struct GLFWDropEvent {
GLFWDropEventType type;
const char **mimes; size_t num_mimes;
// Positions are only valid for GLFW_DROP_ENTER and GLFW_DROP_MOVE.
// They are in window co-ordinates same as for mouse events
double xpos, ypos;
struct { GLFWDragOperationType preferred; int allowed, source_actions; } operation;
bool from_self; // Only valid upto GLFW_DROP_DROP
ssize_t (*read_data)(GLFWwindow *w, struct GLFWDropEvent* ev, char *buffer, size_t sz); // Only valid for GLFW_DROP_DATA_AVAILABLE
void (*finish_drop)(GLFWwindow *w, GLFWDragOperationType op); // Only valid for GLFW_DROP_DROP and GLFW_DROP_DATA_AVAILABLE
} GLFWDropEvent;
typedef void (* GLFWdropeventfun)(GLFWwindow*, GLFWDropEvent *event);
/*! @brief The function pointer type for error callbacks.
*
* This is the function pointer type for error callbacks. An error callback
@@ -1779,7 +1692,7 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int);
*
* @ingroup input
*/
typedef void (* GLFWscrollfun)(GLFWwindow*,const GLFWScrollEvent*);
typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int,int);
/*! @brief The function pointer type for key callbacks.
*
@@ -1813,77 +1726,32 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,const GLFWScrollEvent*);
*/
typedef void (* GLFWkeyboardfun)(GLFWwindow*, GLFWkeyevent*);
typedef enum {
GLFW_DRAG_DATA_REQUEST, // request data for specified mime type
GLFW_DRAG_CANCELLED,
GLFW_DRAG_FINSHED,
GLFW_DRAG_ACCEPTED, // mimetype was accepted or NULL if drag was accepted but no mime type specified
GLFW_DRAG_ACTION_CHANGED, // action was changed 0 or GLFWDragOperationType
GLFW_DRAG_DROPPED, // drop was performed but no data transferred yet
} GLFWDragEventType;
typedef struct GLFWDragSourceItem {
const char *mime_type;
// Can be on null to provide data when the drag is started should be used only when the data is relatively small
const char *optional_data;
size_t data_size;
bool is_remote_client;
int type; // used for file promises type of entry 0 = regular, 1 = symlink, 2 = directory
} GLFWDragSourceItem;
typedef struct GLFWDragEvent {
GLFWDragEventType type;
// When the drag event callback is called with a mimetype and no data, the
// application should set the data ans data_sz and err_num fields.
// Once glfw is done reading the data the drag event callback will be
// called with the data pointer unchanged. The application is now free
// to delete the data, as needed.
const char *mime_type;
const char *data; size_t data_sz;
int err_num; // POSIX error code indicating failure fetching data
GLFWDragOperationType action; // can be 0 indicating no action
bool drop_maybe_a_cancel; // Happens on wayland compositors that dont implement top-level drag
} GLFWDragEvent;
typedef void (* GLFWdragsourcefun)(GLFWwindow* window, GLFWDragEvent *ev);
/*! @brief The function pointer type for drag event callbacks.
/*! @brief The function pointer type for drag and drop callbacks.
*
* This is the function pointer type for drag event callbacks. A drag event
* This is the function pointer type for drop callbacks. A drop
* callback function has the following signature:
* @code
* int function_name(GLFWwindow* window, int event, double xpos, double ypos, const char** mime_types, int* mime_count)
* int function_name(GLFWwindow* window, const char* mime, const char* text)
* @endcode
*
* @param[in] window The window that received the drag event.
* @param[in] event The drag event type: @ref GLFW_DRAG_ENTER, @ref GLFW_DRAG_MOVE,
* or @ref GLFW_DRAG_LEAVE.
* @param[in] xpos The x-coordinate of the drag position in window coordinates.
* @param[in] ypos The y-coordinate of the drag position in window coordinates.
* @param[in,out] mime_types A writable array of MIME type strings available from the drag source.
* For @ref GLFW_DRAG_ENTER and @ref GLFW_DRAG_MOVE events this is non-NULL and contains all
* available MIME types. The callback is responsible for sorting this list by priority and
* keeping only the MIME types it wants to accept. The first MIME type in the sorted list
* will be used for the drop operation. The strings are only valid for the duration of the
* callback; if you need to store them, make copies. For @ref GLFW_DRAG_LEAVE events this
* is `NULL`.
* @param[in,out] mime_count Pointer to the number of MIME types in the array. The callback
* should update this to reflect the new count after sorting and filtering. For
* @ref GLFW_DRAG_LEAVE events this is `NULL`.
* @return For @ref GLFW_DRAG_ENTER and @ref GLFW_DRAG_MOVE events, return non-zero
* to accept the drag or zero to reject it. This allows the application to
* dynamically accept or reject the drag based on the current position.
* Return value is ignored for @ref GLFW_DRAG_LEAVE events.
* @param[in] window The window that received the event.
* @param[in] mime The UTF-8 encoded drop mime-type
* @param[in] data The dropped data or NULL for drag enter events
* @param[in] sz The size of the dropped data
* @return For drag events should return the priority for the specified mime type. A priority of zero
* or lower means the mime type is not accepted. Highest priority will be the finally accepted mime-type.
*
* @sa @ref drag_events
* @sa @ref glfwSetDragCallback
* @sa @ref glfwUpdateDragState
* @pointer_lifetime The text is valid until the
* callback function returns.
*
* @since Added in version 4.0.
* @sa @ref path_drop
* @sa @ref glfwSetDropCallback
*
* @since Added in version 3.1.
*
* @ingroup input
*/
typedef int (* GLFWdragfun)(GLFWwindow*, GLFWDragEventType event, double xpos, double ypos, const char** mime_types, int* mime_count);
typedef int (* GLFWdropfun)(GLFWwindow*, const char *, const char*, size_t);
typedef void (* GLFWliveresizefun)(GLFWwindow*, bool);
@@ -2036,7 +1904,7 @@ typedef struct GLFWimage
int height;
/*! The pixel data of this image, arranged left-to-right, top-to-bottom.
*/
const unsigned char* pixels;
unsigned char* pixels;
} GLFWimage;
/*! @brief Gamepad input state
@@ -2512,7 +2380,6 @@ GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, flo
* @ingroup monitor
*/
GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor);
GLFWAPI const char* glfwGetMonitorDescription(GLFWmonitor* monitor);
/*! @brief Sets the user pointer of the specified monitor.
*
@@ -3007,7 +2874,6 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, G
GLFWAPI bool glfwToggleFullscreen(GLFWwindow *window, unsigned int flags);
GLFWAPI bool glfwIsFullscreen(GLFWwindow *window, unsigned int flags);
GLFWAPI bool glfwAreSwapsAllowed(const GLFWwindow* window);
GLFWAPI const GLFWLayerShellConfig* glfwGetLayerShellConfig(GLFWwindow* handle);
GLFWAPI bool glfwSetLayerShellConfig(GLFWwindow* handle, const GLFWLayerShellConfig *value);
/*! @brief Destroys the specified window and its context.
@@ -3705,7 +3571,7 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window);
*
* @ingroup window
*/
GLFWAPI void glfwShowWindow(GLFWwindow* window, bool move_to_active_screen);
GLFWAPI void glfwShowWindow(GLFWwindow* window);
/*! @brief Hides the specified window.
*
@@ -4373,8 +4239,6 @@ GLFWAPI void glfwPostEmptyEvent(void);
GLFWAPI bool glfwGetIgnoreOSKeyboardProcessing(void);
GLFWAPI void glfwSetIgnoreOSKeyboardProcessing(bool enabled);
GLFWAPI bool glfwGrabKeyboard(int grab);
GLFWAPI void glfwGetKeyboardRepeatDelay(monotonic_t *delay, monotonic_t *interval);
/*! @brief Returns the value of an input option for the specified window.
*
@@ -4978,20 +4842,44 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcu
*/
GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback);
/*! @brief Sets the path drop callback.
*
* This function sets the path drop callback of the specified window, which is
* called when one or more dragged paths are dropped on the window.
*
* Because the path array and its strings may have been generated specifically
* for that event, they are not guaranteed to be valid after the callback has
* returned. If you wish to use them after the callback returns, you need to
* make a deep copy.
*
* @param[in] window The window whose callback to set.
* @param[in] callback The new file drop callback, or `NULL` to remove the
* currently set callback.
* @return The previously set callback, or `NULL` if no callback was set or the
* library had not been [initialized](@ref intro_init).
*
* @callback_signature
* @code
* void function_name(GLFWwindow* window, int path_count, const char* paths[])
* @endcode
* For more information about the callback parameters, see the
* [function pointer type](@ref GLFWdropfun).
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @remark @wayland File drop is currently unimplemented.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref path_drop
*
* @since Added in version 3.1.
*
* @ingroup input
*/
GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback);
GLFWAPI GLFWliveresizefun glfwSetLiveResizeCallback(GLFWwindow* window, GLFWliveresizefun callback);
GLFWAPI GLFWdropeventfun glfwSetDropEventCallback(GLFWwindow *window, GLFWdropeventfun callback);
// ask for update before GLFW_DROP_DROP happens
GLFWAPI void glfwRequestDropUpdate(GLFWwindow *window);
GLFWAPI int glfwRequestDropData(GLFWwindow *window, const char *mime);
GLFWAPI void glfwEndDrop(GLFWwindow *window, GLFWDragOperationType op);
GLFWAPI GLFWdragsourcefun glfwSetDragSourceCallback(GLFWwindow* window, GLFWdragsourcefun callback);
// Start a drag. If called with operations == -1 indicates that previously
// requested data via GLFW_DRAG_DATA_REQUEST is ready. operations == -2 means
// that the drag image is changed. operations == -3 cancels any existing drag.
GLFWAPI int glfwStartDrag(GLFWwindow* window, const GLFWDragSourceItem *items, size_t mime_count, const GLFWimage* thumbnail, int operations, bool needs_toplevel_on_wayland);
/*! @brief Returns whether the specified joystick is present.
*
* This function returns whether the specified joystick is present.

4
glfw/glx_context.c vendored
View File

@@ -658,8 +658,6 @@ bool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig UNUSED,
GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
if (window->context.client == GLFW_NO_API)
@@ -674,8 +672,6 @@ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* handle)
GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(None);
if (window->context.client == GLFW_NO_API)

4
glfw/ibus_glfw.c vendored
View File

@@ -287,9 +287,7 @@ get_ibus_address_file_name(void) {
addr = getenv("IBUS_ADDRESS");
int offset = 0;
if (addr && addr[0]) {
size_t len = GLFW_MIN(strlen(addr), sizeof(ans) - 1);
memcpy(ans, addr, len);
ans[len] = '\0';
memcpy(ans, addr, GLFW_MIN(strlen(addr), sizeof(ans)));
return ans;
}
const char* disp_num = NULL;

168
glfw/input.c vendored
View File

@@ -31,16 +31,11 @@
#include "../kitty/monotonic.h"
#include <assert.h>
#include <errno.h>
#include <float.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#define NAME mime_dedup_set
#define KEY_TY const char *
#include "../3rdparty/verstable.h"
// Internal key state used for sticky keys
#define _GLFW_STICK 3
@@ -358,10 +353,10 @@ void _glfwInputKeyboard(_GLFWwindow* window, GLFWkeyevent* ev)
// Notifies shared code of a scroll event
//
void _glfwInputScroll(_GLFWwindow* window, const GLFWScrollEvent *ev)
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags, int mods)
{
if (window->callbacks.scroll)
window->callbacks.scroll((GLFWwindow*) window, ev);
window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset, flags, mods);
}
// Notifies shared code of a mouse button click event
@@ -406,58 +401,13 @@ void _glfwInputCursorEnter(_GLFWwindow* window, bool entered)
window->callbacks.cursorEnter((GLFWwindow*) window, entered);
}
// Notifies shared code of a drop event.
// The caller is responsible for passing a mutable working-copy of the mimes
// array (reset to the full original list before each call) so that the
// callback can sort/filter in-place without touching the backend's canonical
// storage. The return value is ev.num_mimes after the callback returns,
// i.e. the number of accepted (possibly reordered) mimes starting at
// mimes[0].
size_t _glfwInputDropEvent(_GLFWwindow *window, GLFWDropEventType type, double xpos, double ypos, const char** mimes, size_t num_mimes, bool from_self) {
if (!window->callbacks.drop_event) return 0;
if (num_mimes > 1) {
mime_dedup_set seen;
mime_dedup_set_init(&seen);
size_t write_pos = 0;
for (size_t i = 0; i < num_mimes; i++) {
if (mime_dedup_set_is_end(mime_dedup_set_get(&seen, mimes[i]))) {
if (mime_dedup_set_is_end(mime_dedup_set_insert(&seen, mimes[i]))) {
// OOM: shift remaining entries to write_pos and stop deduplicating
memmove(mimes + write_pos, mimes + i, (num_mimes - i) * sizeof(const char*));
write_pos += num_mimes - i;
break;
}
// Swap the unique entry into write_pos; the displaced entry moves to
// position i where it will end up at or beyond the new num_mimes.
if (write_pos != i) {
const char *tmp = mimes[write_pos];
mimes[write_pos] = mimes[i];
mimes[i] = tmp;
}
write_pos++;
}
// duplicate: left in-place; will end up at a position >= new num_mimes
}
num_mimes = write_pos;
mime_dedup_set_cleanup(&seen);
}
GLFWDropEvent ev = {
.mimes=mimes, .type=type, .xpos=xpos, .ypos=ypos, .num_mimes=num_mimes, .from_self=from_self,
.read_data=type == GLFW_DROP_DATA_AVAILABLE ? _glfwPlatformReadAvailableDropData : NULL,
.finish_drop=type == GLFW_DROP_DATA_AVAILABLE || type == GLFW_DROP_DROP ? _glfwPlatformEndDrop : NULL,
.operation.allowed = window->drop_operation.allowed, .operation.preferred = window->drop_operation.preferred,
.operation.source_actions = window->drop_operation.source_actions,
};
window->callbacks.drop_event((GLFWwindow*)window, &ev);
window->drop_operation.preferred = ev.operation.preferred; window->drop_operation.allowed = ev.operation.allowed;
return ev.num_mimes;
}
// Notifies shared code that the OS wants data for a MIME type from the drag source
// Notifies shared code of files or directories dropped on a window
//
void _glfwInputDragSourceRequest(_GLFWwindow* window, GLFWDragEvent *ev) {
if (window->callbacks.drag_source)
window->callbacks.drag_source((GLFWwindow*) window, ev);
int _glfwInputDrop(_GLFWwindow* window, const char *mime, const char *text, size_t sz)
{
if (window->callbacks.drop)
return window->callbacks.drop((GLFWwindow*) window, mime, text, sz);
return 0;
}
// Notifies shared code of a joystick connection or disconnection
@@ -726,18 +676,6 @@ void _glfwCenterCursorInContentArea(_GLFWwindow* window)
////// GLFW public API //////
//////////////////////////////////////////////////////////////////////////
GLFWAPI int glfwRequestDropData(GLFWwindow *window, const char *mime) {
return _glfwPlatformRequestDropData((_GLFWwindow*)window, mime);
}
GLFWAPI void glfwEndDrop(GLFWwindow *window, GLFWDragOperationType op) {
_glfwPlatformEndDrop(window, op);
}
GLFWAPI void glfwRequestDropUpdate(GLFWwindow *window) {
_glfwPlatformRequestDropUpdate((_GLFWwindow*)window);
}
GLFWAPI bool glfwGetIgnoreOSKeyboardProcessing(void) {
return _glfw.ignoreOSKeyboardProcessing;
}
@@ -746,20 +684,6 @@ GLFWAPI void glfwSetIgnoreOSKeyboardProcessing(bool enabled) {
_glfw.ignoreOSKeyboardProcessing = enabled;
}
GLFWAPI bool glfwGrabKeyboard(int grab) {
if (grab == 0 || grab == 1) {
if (_glfwPlatformGrabKeyboard(grab)) _glfw.keyboard_grabbed = grab;
}
return _glfw.keyboard_grabbed;
}
GLFWAPI void glfwGetKeyboardRepeatDelay(monotonic_t *delay, monotonic_t *interval) {
_GLFW_REQUIRE_INIT();
if (delay) *delay = ms_to_monotonic_t(500ll);
if (interval) *interval = ms_to_monotonic_t(30ll);
_glfwPlatformGetKeyboardRepeatDelay(delay, interval);
}
GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
@@ -1170,88 +1094,16 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle,
return cbfun;
}
GLFWAPI GLFWdropeventfun glfwSetDropEventCallback(GLFWwindow* handle, GLFWdropeventfun cbfun)
GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
_GLFW_SWAP_POINTERS(window->callbacks.drop_event, cbfun);
_GLFW_SWAP_POINTERS(window->callbacks.drop, cbfun);
return cbfun;
}
GLFWAPI GLFWdragsourcefun glfwSetDragSourceCallback(GLFWwindow* handle, GLFWdragsourcefun cbfun)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
_GLFW_SWAP_POINTERS(window->callbacks.drag_source, cbfun);
return cbfun;
}
void
_glfwFreeDragSourceData(void) {
GLFWid drag_window_id = _glfw.drag.window_id;
_glfwPlatformFreeDragSourceData();
if (_glfw.drag.items) {
for (size_t i = 0; i < _glfw.drag.item_count; i++) {
free((void*)_glfw.drag.items[i].mime_type);
free((void*)_glfw.drag.items[i].optional_data);
}
free(_glfw.drag.items);
}
GLFWid iid = _glfw.drag.instance_id;
memset(&_glfw.drag, 0, sizeof(_glfw.drag));
_glfw.drag.instance_id = iid;
// Send a synthetic left button release to the drag source window if the
// button is still marked as pressed. The focus-loss release was suppressed
// while the drag was in progress, and on some platforms (Wayland, Cocoa)
// the OS never sends a natural button release after the drag ends.
if (drag_window_id) {
_GLFWwindow* drag_window = _glfwWindowForId(drag_window_id);
if (drag_window && drag_window->mouseButtons[GLFW_MOUSE_BUTTON_LEFT] == GLFW_PRESS)
_glfwInputMouseClick(drag_window, GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, 0);
}
}
GLFWAPI int
glfwStartDrag(GLFWwindow* handle, const GLFWDragSourceItem *items, size_t item_count, const GLFWimage* thumbnail, int operations, bool needs_toplevel_on_wayland) {
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(EINVAL);
if (operations == -1) return _glfwPlatformDragDataReady(items[0].mime_type, items[0].optional_data, items[0].data_size, items[0].type);
if (operations == -2) return _glfwPlatformChangeDragImage(thumbnail);
if (operations == -3) { _glfwPlatformCancelDrag(window); return 0; }
_glfwFreeDragSourceData();
_glfw.drag.instance_id++;
if (!items || !item_count) return 0;
_glfw.drag.items = calloc(item_count, sizeof(_glfw.drag.items[0]));
if (!_glfw.drag.items) return ENOMEM;
_glfw.drag.item_count = item_count;
for (size_t i = 0; i < item_count; i++) {
if (!items[i].mime_type || !items[i].mime_type[0]) {
_glfwFreeDragSourceData(); return EINVAL;
}
_glfw.drag.items[i].mime_type = _glfw_strdup(items[i].mime_type);
if (!_glfw.drag.items[i].mime_type) { _glfwFreeDragSourceData(); return ENOMEM; }
if (items[i].optional_data) {
_glfw.drag.items[i].optional_data = malloc(items[i].data_size);
if (!_glfw.drag.items[i].optional_data) { _glfwFreeDragSourceData(); return ENOMEM; }
memcpy((void*)_glfw.drag.items[i].optional_data, items[i].optional_data, items[i].data_size);
}
_glfw.drag.items[i].data_size = items[i].data_size;
_glfw.drag.items[i].is_remote_client = items[i].is_remote_client;
}
_glfw.drag.window_id = window->id;
_glfw.drag.operations = operations;
_glfw.drag.needs_toplevel_on_wayland = needs_toplevel_on_wayland;
int ans = _glfwPlatformStartDrag(window, thumbnail);
if (ans != 0) _glfwFreeDragSourceData();
return ans;
}
GLFWAPI int glfwJoystickPresent(int jid)
{
_GLFWjoystick* js;

50
glfw/internal.h vendored
View File

@@ -331,7 +331,7 @@ struct _GLFWwndconfig
char instanceName[256];
} x11;
struct {
char appId[256], windowTag[256];
char appId[256];
uint32_t bgcolor;
} wl;
};
@@ -461,7 +461,6 @@ struct _GLFWwindow
#else
const bool swaps_disallowed;
#endif
struct { GLFWDragOperationType preferred; int allowed, source_actions; } drop_operation;
struct {
GLFWwindowposfun pos;
@@ -479,10 +478,8 @@ struct _GLFWwindow
GLFWcursorenterfun cursorEnter;
GLFWscrollfun scroll;
GLFWkeyboardfun keyboard;
GLFWdropfun drop;
GLFWliveresizefun liveResize;
GLFWdragsourcefun drag_source;
GLFWdropeventfun drop_event;
} callbacks;
// This is defined in the window API's platform.h
@@ -493,7 +490,7 @@ struct _GLFWwindow
//
struct _GLFWmonitor
{
const char *name, *description;
char* name;
void* userPointer;
// Physical dimensions in millimeters.
@@ -619,7 +616,7 @@ struct _GLFWlibrary
_GLFWtls contextSlot;
_GLFWmutex errorLock;
bool ignoreOSKeyboardProcessing, keyboard_grabbed;
bool ignoreOSKeyboardProcessing;
struct {
bool available;
@@ -665,12 +662,6 @@ struct _GLFWlibrary
_GLFWlibraryEGL egl;
// This is defined in osmesa_context.h
_GLFWlibraryOSMesa osmesa;
struct {
GLFWDragSourceItem *items; size_t item_count;
GLFWid window_id, instance_id; int operations;
bool needs_toplevel_on_wayland;
} drag;
};
// Global state shared between compilation units of GLFW
@@ -728,7 +719,6 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title);
void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
int count, const GLFWimage* images);
bool _glfwPlatformSetLayerShellConfig(_GLFWwindow* window, const GLFWLayerShellConfig *value);
const GLFWLayerShellConfig* _glfwPlatformGetLayerShellConfig(_GLFWwindow* window);
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos);
void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos);
void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height);
@@ -746,11 +736,10 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
float* xscale, float* yscale);
monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window);
void _glfwPlatformGetKeyboardRepeatDelay(monotonic_t *delay, monotonic_t *interval);
void _glfwPlatformIconifyWindow(_GLFWwindow* window);
void _glfwPlatformRestoreWindow(_GLFWwindow* window);
void _glfwPlatformMaximizeWindow(_GLFWwindow* window);
void _glfwPlatformShowWindow(_GLFWwindow* window, bool move_to_active_screen);
void _glfwPlatformShowWindow(_GLFWwindow* window);
void _glfwPlatformHideWindow(_GLFWwindow* window);
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window);
int _glfwPlatformWindowBell(_GLFWwindow* window);
@@ -824,26 +813,11 @@ void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
void _glfwInputKeyboard(_GLFWwindow *window, GLFWkeyevent *ev);
void _glfwInputClipboardLost(GLFWClipboardType which);
void _glfwInputScroll(_GLFWwindow* window, const GLFWScrollEvent *ev);
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags, int mods);
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
void _glfwInputCursorEnter(_GLFWwindow* window, bool entered);
// Platform functions for drop data reading
void _glfwPlatformRequestDropUpdate(_GLFWwindow* window);
size_t _glfwInputDropEvent(_GLFWwindow *window, GLFWDropEventType type, double xpos, double ypos, const char** mimes, size_t num_mimes, bool from_self);
ssize_t _glfwPlatformReadAvailableDropData(GLFWwindow *w, GLFWDropEvent *ev, char *buffer, size_t sz);
void _glfwPlatformEndDrop(GLFWwindow *w, GLFWDragOperationType op);
int _glfwPlatformRequestDropData(_GLFWwindow *window, const char *mime);
// Platform functions for drag source
int _glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail);
void _glfwPlatformCancelDrag(_GLFWwindow* window);
void _glfwFreeDragSourceData(void);
void _glfwPlatformFreeDragSourceData(void);
void _glfwInputDragSourceRequest(_GLFWwindow* window, GLFWDragEvent *ev);
int _glfwPlatformDragDataReady(const char *mime_type, const char *data, size_t sz, int type);
int _glfwPlatformChangeDragImage(const GLFWimage *thumbnail);
int _glfwInputDrop(_GLFWwindow* window, const char *mime, const char *text, size_t sz);
void _glfwInputColorScheme(GLFWColorScheme, bool);
void _glfwPlatformInputColorScheme(GLFWColorScheme);
void _glfwInputJoystick(_GLFWjoystick* js, int event);
@@ -909,15 +883,6 @@ void _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval,
void _glfwPlatformRemoveTimer(unsigned long long timer_id);
int _glfwPlatformSetWindowBlur(_GLFWwindow* handle, int value);
MonitorGeometry _glfwPlatformGetMonitorGeometry(_GLFWmonitor* monitor);
bool _glfwPlatformGrabKeyboard(bool grab);
void glfw_handle_scroll_event_for_momentum(_GLFWwindow *w, const GLFWScrollEvent *ev, bool stopped, bool is_finger_based);
#define glfw_cancel_momentum_scroll() glfw_handle_scroll_event_for_momentum(NULL, NULL, false, false)
#ifdef _GLFW_X11
#define momentum_scroll_gesture_detection_timeout_ms 50
#else
#define momentum_scroll_gesture_detection_timeout_ms 0
#endif
bool glfw_is_momentum_scroll_enabled(void);
char* _glfw_strdup(const char* source);
@@ -925,4 +890,3 @@ void _glfw_free_clipboard_data(_GLFWClipboardData *cd);
#define debug_rendering(...) if (_glfw.hints.init.debugRendering) { timed_debug_print(__VA_ARGS__); }
#define debug_input(...) if (_glfw.hints.init.debugKeyboard) { timed_debug_print(__VA_ARGS__); }
#define safe_close(fd) do { errno = 0; close(fd); } while(errno == EINTR)

View File

@@ -259,7 +259,7 @@ glfw_initialize_desktop_settings(void) {
get_cursor_theme_from_env();
DBusConnection *session_bus = glfw_dbus_session_bus();
if (session_bus) {
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.");
if (!read_desktop_settings(session_bus)) _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to read desktop settings, make sure you have the desktop portal running.");
dbus_bus_add_match(session_bus, "type='signal',interface='" DESKTOP_INTERFACE "',member='SettingChanged'", NULL);
dbus_connection_add_filter(session_bus, setting_changed, NULL, NULL);
}

227
glfw/momentum-scroll.c vendored
View File

@@ -1,227 +0,0 @@
/*
* momentum-scroll.c
* Copyright (C) 2026 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "internal.h"
#include <math.h>
typedef struct ScrollSample {
double dx, dy;
monotonic_t timestamp;
} ScrollSample;
#define DEQUE_DATA_TYPE ScrollSample
#define DEQUE_NAME ScrollSamples
#include "../kitty/fixed_size_deque.h"
typedef enum ScrollerState { NONE, PHYSICAL_EVENT_IN_PROGRESS, MOMENTUM_IN_PROGRESS } ScrollerState;
typedef struct MomentumScroller {
double friction, // Deceleration inverse factor (0-1, higher = longer coast)
min_velocity, // Minimum velocity before stopping
max_velocity, // Maximum velocity to prevent runaway scrolling
velocity_scale; // Scale factor for initial velocity
monotonic_t timer_interval; // animation speed
GLFWid timer_id, window_id;
ScrollSamples samples;
ScrollerState state;
double scale;
struct { double x, y; } velocity;
int keyboard_modifiers;
struct {
monotonic_t start, duration;
struct { double x, y; } displacement;
} physical_event;
} MomentumScroller;
#define DEFAULTS { .friction = 0.96, .min_velocity = 0.5, .max_velocity = 100, .timer_interval = 10, }
static const MomentumScroller defaults = DEFAULTS;
static MomentumScroller s = DEFAULTS;
#undef DEFAULTS
GLFWAPI void
glfwConfigureMomentumScroller(double friction, double min_velocity, double max_velocity, unsigned timer_interval_ms) {
s.timer_interval = timer_interval_ms ? ms_to_monotonic_t(timer_interval_ms) : defaults.timer_interval;
s.friction = friction < 0 ? defaults.friction : MAX(0, MIN(friction, 1));
#define S(w) s.w = w >= 0 ? w : defaults.w
S(min_velocity); S(max_velocity);
#undef S
}
static void
cancel_existing_scroll(bool reset_velocity) {
if (s.timer_id) {
glfwRemoveTimer(s.timer_id);
s.timer_id = 0;
}
if (s.state == MOMENTUM_IN_PROGRESS) {
_GLFWwindow *w = _glfwWindowForId(s.window_id);
if (w) _glfwInputScroll(
w, &(GLFWScrollEvent){.momentum_type=GLFW_MOMENTUM_PHASE_CANCELED, .keyboard_modifiers=s.keyboard_modifiers});
}
s.window_id = 0;
s.keyboard_modifiers = 0;
deque_clear(&s.samples);
s.state = NONE;
if (reset_velocity) { s.velocity.x = 0; s.velocity.y = 0; }
}
static void
add_sample(double dx, double dy, monotonic_t now) {
deque_push_back(&s.samples, (ScrollSample){dx, dy, now}, NULL);
}
static void
last_sample_delta(double *dx, double *dy) {
const ScrollSample *ss;
if ((ss = deque_peek_back(&s.samples))) { *dx = ss->dx; *dy = ss->dy; }
else { *dx = 0; *dy = 0; }
}
static void
trim_old_samples(monotonic_t now) {
const ScrollSample *ss;
while ((ss = deque_peek_front(&s.samples)) && (now - ss->timestamp) > ms_to_monotonic_t(150))
deque_pop_front(&s.samples, NULL);
}
static void
add_velocity(double x, double y) {
if (x == 0 || x * s.velocity.x >= 0) s.velocity.x += x;
else s.velocity.x = x;
if (y == 0 || y * s.velocity.y >= 0) s.velocity.y += y;
else s.velocity.y = y;
s.velocity.x = MAX(-s.max_velocity, MIN(s.velocity.x, s.max_velocity));
s.velocity.y = MAX(-s.max_velocity, MIN(s.velocity.y, s.max_velocity));
}
static void
set_velocity_from_samples(monotonic_t now) {
s.timer_interval = ms_to_monotonic_t(8);
trim_old_samples(now);
ScrollSample ss;
switch (deque_size(&s.samples)) {
case 0:
return;
case 1:
deque_pop_front(&s.samples, &ss);
add_velocity(ss.dx, ss.dy);
return;
}
// Use weighted average - more recent samples have higher weight
double total_dx = 0.0, total_dy = 0.0, total_weight = 0.0;
monotonic_t first_time = deque_peek_front(&s.samples)->timestamp;
monotonic_t last_time = deque_peek_back(&s.samples)->timestamp;
double time_span = MAX(1, last_time - first_time);
for (size_t i = 0; i < deque_size(&s.samples); i++) {
const ScrollSample *ss = deque_at(&s.samples, i);
double weight = 1.0 + (ss->timestamp - first_time) / time_span;
total_dx += ss->dx * weight; total_dy += ss->dy * weight;
total_weight += weight;
}
deque_clear(&s.samples);
if (total_weight <= 0) return;
double dy = total_dy / total_weight, dx = total_dx / total_weight;
add_velocity(dx, dy);
if (false) timed_debug_print("momentum scroll: event velocity: %.1f final velocity: %.1f\n", dy, s.velocity.y);
}
static void
send_momentum_event(bool is_start) {
_GLFWwindow *w = _glfwWindowForId(s.window_id);
if (!w || w != _glfwFocusedWindow()) {
cancel_existing_scroll(true);
return;
}
s.velocity.x *= s.friction; s.velocity.y *= s.friction;
if (fabs(s.velocity.x) < s.min_velocity) s.velocity.x = 0;
if (fabs(s.velocity.y) < s.min_velocity) s.velocity.y = 0;
GLFWMomentumType m = is_start ? GLFW_MOMENTUM_PHASE_BEGAN : GLFW_MOMENTUM_PHASE_ACTIVE;
if (s.velocity.x == 0 && s.velocity.y == 0 && !is_start) {
m = GLFW_MOMENTUM_PHASE_ENDED;
if (s.timer_id) glfwRemoveTimer(s.timer_id);
s.timer_id = 0;
s.state = NONE;
}
GLFWScrollEvent e = {
.offset_type=GLFW_SCROLL_OFFEST_HIGHRES, .momentum_type=m, .unscaled.x=s.velocity.x, .unscaled.y=s.velocity.y,
.x_offset=s.scale * s.velocity.x, .y_offset=s.scale * s.velocity.y, .keyboard_modifiers=s.keyboard_modifiers
};
_glfwInputScroll(w, &e);
}
static void
momentum_timer_fired(unsigned long long timer_id UNUSED, void *data UNUSED) {
send_momentum_event(false);
}
static void
start_momentum_scroll(monotonic_t now) {
set_velocity_from_samples(now);
send_momentum_event(true);
s.timer_id = glfwAddTimer(s.timer_interval, true, momentum_timer_fired, NULL, NULL);
}
static bool
is_suitable_for_momentum(void) {
return (
MAX(fabs(s.physical_event.displacement.x), fabs(s.physical_event.displacement.y)) > 10 &&
s.physical_event.duration > ms_to_monotonic_t(2)
);
}
bool
glfw_is_momentum_scroll_enabled(void) {
return s.friction > 0;
}
void
glfw_handle_scroll_event_for_momentum(
_GLFWwindow *w, const GLFWScrollEvent *ev, bool stopped, bool is_finger_based
) {
const bool is_synthetic_momentum_start_event = stopped && momentum_scroll_gesture_detection_timeout_ms;
if (!w) { cancel_existing_scroll(true); return; }
if (!is_finger_based || ev->offset_type != GLFW_SCROLL_OFFEST_HIGHRES || !glfw_is_momentum_scroll_enabled()) {
if (ev->x_offset != 0 || ev->y_offset != 0) _glfwInputScroll(w, ev);
return;
}
monotonic_t now = monotonic();
if (is_synthetic_momentum_start_event) now -= ms_to_monotonic_t(momentum_scroll_gesture_detection_timeout_ms);
if (s.state == PHYSICAL_EVENT_IN_PROGRESS) {
s.physical_event.displacement.x += ev->unscaled.x;
s.physical_event.displacement.y += ev->unscaled.y;
if (stopped) {
s.physical_event.duration = now - s.physical_event.start;
s.physical_event.start = 0;
}
} else {
memset(&s.physical_event, 0, sizeof(s.physical_event));
s.physical_event.start = now;
}
if (ev->unscaled.y > 0) s.scale = ev->y_offset / ev->unscaled.y;
else if (ev->unscaled.x > 0) s.scale = ev->x_offset / ev->unscaled.x;
if (s.window_id && s.window_id != w->id) cancel_existing_scroll(true);
if (s.state != PHYSICAL_EVENT_IN_PROGRESS) cancel_existing_scroll(false);
if (!is_synthetic_momentum_start_event) {
// Check for change in direction
double ldx, ldy; last_sample_delta(&ldx, &ldy);
if (ldx * ev->x_offset < 0 || ldy * ev->y_offset < 0) cancel_existing_scroll(true);
}
s.window_id = w->id;
s.keyboard_modifiers = ev->keyboard_modifiers;
if (ev->offset_type == GLFW_SCROLL_OFFEST_HIGHRES) {
if (!is_synthetic_momentum_start_event) add_sample(ev->unscaled.x, ev->unscaled.y, now);
if (stopped) s.state = is_suitable_for_momentum() ? MOMENTUM_IN_PROGRESS : NONE;
else s.state = PHYSICAL_EVENT_IN_PROGRESS;
} else {
s.state = stopped ? NONE : PHYSICAL_EVENT_IN_PROGRESS;
}
if (s.state == MOMENTUM_IN_PROGRESS) start_momentum_scroll(now);
else _glfwInputScroll(w, ev);
}

15
glfw/monitor.c vendored
View File

@@ -186,8 +186,7 @@ void _glfwFreeMonitor(_GLFWmonitor* monitor)
_glfwFreeGammaArrays(&monitor->currentRamp);
free(monitor->modes);
free((void*)monitor->name);
free((void*)monitor->description);
free(monitor->name);
free(monitor);
}
@@ -394,19 +393,9 @@ GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle)
assert(monitor != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return monitor->name ? monitor->name : "";
return monitor->name;
}
GLFWAPI const char* glfwGetMonitorDescription(GLFWmonitor* handle)
{
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
assert(monitor != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
return monitor->description ? monitor->description : "";
}
GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer)
{
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;

View File

@@ -27,7 +27,6 @@
//========================================================================
#include "internal.h"
#include <assert.h>
static void makeContextCurrentNSGL(_GLFWwindow* window)
@@ -107,22 +106,12 @@ bool _glfwInitNSGL(void)
return true;
}
// We use a globally shared context across all OS Windows so that
// if all OS Windows are closed the context does not have to be recreated
// with all associated data lost. On Apple kitty can remain running with no
// OS Windows.
static NSOpenGLContext* globally_shared_context = nil;
// Terminate OpenGL support
//
void _glfwTerminateNSGL(void) {
if (globally_shared_context != nil) {
[globally_shared_context release];
globally_shared_context = nil;
}
void _glfwTerminateNSGL(void)
{
}
// Create the OpenGL context
//
bool _glfwCreateContextNSGL(_GLFWwindow* window,
@@ -282,7 +271,7 @@ bool _glfwCreateContextNSGL(_GLFWwindow* window,
return false;
}
NSOpenGLContext* share = globally_shared_context;
NSOpenGLContext* share = nil;
if (ctxconfig->share)
share = ctxconfig->share->context.nsgl.object;
@@ -296,9 +285,6 @@ bool _glfwCreateContextNSGL(_GLFWwindow* window,
"NSGL: Failed to create OpenGL context");
return false;
}
if (globally_shared_context == nil) {
globally_shared_context = [window->context.nsgl.object retain];
}
if (fbconfig->transparent)
{
@@ -333,8 +319,6 @@ bool _glfwCreateContextNSGL(_GLFWwindow* window,
GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(nil);
if (window->context.client == GLFW_NO_API)
@@ -345,48 +329,3 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle)
return window->context.nsgl.object;
}
GLFWAPI bool
glfwCocoaRecreateGLDrawable(GLFWwindow *w) {
_GLFWwindow* window = (_GLFWwindow*)w;
if (window->context.client == GLFW_NO_API) return false;
@try {
// Save current state
NSOpenGLPixelFormat *pixelFormat = window->context.nsgl.pixelFormat;
NSOpenGLContext *oldContext = window->context.nsgl.object;
// Create a new context sharing resources with the old one
NSOpenGLContext *newContext = [[NSOpenGLContext alloc]
initWithFormat:pixelFormat
shareContext:oldContext];
if (newContext == nil) return false;
// Copy settings from old context
GLint opacity = 0;
[oldContext getValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
[newContext setValues:&opacity forParameter:NSOpenGLContextParameterSurfaceOpacity];
GLint interval = 0;
[newContext setValues:&interval forParameter:NSOpenGLContextParameterSwapInterval];
// Detach old context
[NSOpenGLContext clearCurrentContext];
[oldContext clearDrawable];
// Attach new context to the view
[window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[newContext setView:window->ns.view];
#pragma clang diagnostic pop
[newContext makeCurrentContext];
[newContext update];
// Replace context
window->context.nsgl.object = newContext;
[oldContext release];
} @catch (NSException *e) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to recreate NSGL context: %s (%s)", [[e name] UTF8String], [[e reason] UTF8String]);
return false;
}
return true;
}

41
glfw/null_window.c vendored
View File

@@ -30,7 +30,6 @@
#include "internal.h"
#include "../kitty/monotonic.h"
#include <errno.h>
#include <stdlib.h>
static void applySizeLimits(_GLFWwindow* window, int* width, int* height)
@@ -311,12 +310,6 @@ monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
return ms_to_monotonic_t(500ll);
}
void _glfwPlatformGetKeyboardRepeatDelay(monotonic_t *delay, monotonic_t *interval)
{
if (delay) *delay = ms_to_monotonic_t(500ll);
if (interval) *interval = ms_to_monotonic_t(30ll);
}
void _glfwPlatformIconifyWindow(_GLFWwindow* window)
{
if (_glfw.null.focusedWindow == window)
@@ -537,40 +530,6 @@ void _glfwPlatformSetCursor(_GLFWwindow* window UNUSED, _GLFWcursor* cursor UNUS
{
}
void _glfwPlatformCancelDrag(_GLFWwindow* window UNUSED)
{
// No-op for null platform
}
int
_glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) {
(void)window; (void)thumbnail;
return ENOTSUP;
}
void _glfwPlatformFreeDragSourceData(void) {}
int
_glfwPlatformDragDataReady(const char *mime_type UNUSED, const char *data UNUSED, size_t sz UNUSED, int type UNUSED) { return 0; }
int _glfwPlatformChangeDragImage(const GLFWimage *thumbnail) { (void)thumbnail; return 0; }
const char** _glfwPlatformGetDropMimeTypes(GLFWDropData* drop UNUSED, int* count)
{
if (count) *count = 0;
return NULL;
}
ssize_t _glfwPlatformReadDropData(GLFWDropData* drop UNUSED, const char* mime UNUSED,
void* buffer UNUSED, size_t capacity UNUSED,
monotonic_t timeout UNUSED)
{
return -ENOENT;
}
void _glfwPlatformFinishDrop(GLFWDropData* drop, GLFWDragOperationType operation UNUSED, bool success UNUSED)
{
if (!drop) return;
// Free the heap-allocated drop data structure
free(drop);
}
void _glfwPlatformSetClipboardString(const char* string)
{
char* copy = _glfw_strdup(string);

View File

@@ -352,8 +352,6 @@ GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle,
GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
if (window->context.client == GLFW_NO_API)

View File

@@ -84,13 +84,8 @@
"staging/fractional-scale/fractional-scale-v1.xml",
"staging/single-pixel-buffer/single-pixel-buffer-v1.xml",
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml",
"unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml",
"staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml",
"staging/xdg-system-bell/xdg-system-bell-v1.xml",
"staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml",
"staging/ext-background-effect/ext-background-effect-v1.xml",
"staging/xdg-toplevel-drag/xdg-toplevel-drag-v1.xml",
"unstable/pointer-gestures/pointer-gestures-unstable-v1.xml",
"kwin-blur-v1.xml",
"wlr-layer-shell-unstable-v1.xml"
@@ -112,7 +107,6 @@
"linux_joystick.c",
"linux_desktop_settings.c",
"null_joystick.c",
"momentum-scroll.c",
"linux_notify.c"
]
},
@@ -172,7 +166,6 @@
"linux_joystick.c",
"null_joystick.c",
"linux_desktop_settings.c",
"momentum-scroll.c",
"linux_notify.c"
]
}

Some files were not shown because too many files have changed in this diff Show More