Compare commits

..

145 Commits
v0.38.1 ... var

Author SHA1 Message Date
Kovid Goyal
656d2179c9 ... 2024-06-22 11:33:22 +05:30
Kovid Goyal
da582b5622 ... 2024-06-22 11:20:54 +05:30
Kovid Goyal
ba4292e912 More choose-fonts documentation 2024-06-22 11:20:54 +05:30
Kovid Goyal
785726d21d Sort style names by variant axis value when available 2024-06-22 11:20:54 +05:30
Kovid Goyal
e3a155266e Fix marking of current style in list 2024-06-22 11:20:54 +05:30
Kovid Goyal
e3eb179be2 Fix matching against style names in presence of elision 2024-06-22 11:20:54 +05:30
Kovid Goyal
e88ae3397f Start documenting the choose fonts kitten 2024-06-22 11:20:54 +05:30
Kovid Goyal
8e0ef0c430 Fix spec generation for auto setting 2024-06-22 11:20:54 +05:30
Kovid Goyal
02df66733e Fix changing styles discarding features 2024-06-22 11:20:54 +05:30
Kovid Goyal
4a038ea581 Preserve auto setting when re-running choose-fonts on already selected family 2024-06-22 11:20:54 +05:30
Kovid Goyal
e41d57dffd Output features in spec_from_face 2024-06-22 11:20:54 +05:30
Kovid Goyal
b63be88bac ... 2024-06-22 11:20:54 +05:30
Kovid Goyal
8bfff51d23 Automatically propagate features from regular face to the other faces when they are set to auto 2024-06-22 11:20:54 +05:30
Kovid Goyal
72268539ef Fix sorting of features in UI 2024-06-22 11:20:54 +05:30
Kovid Goyal
5b83a33888 Setting of index features now basically works 2024-06-22 11:20:54 +05:30
Kovid Goyal
daaec1b47f ... 2024-06-22 11:20:53 +05:30
Kovid Goyal
bc56fce38d Add support for font features when rendering sample text 2024-06-22 11:20:53 +05:30
Kovid Goyal
788b3dc4b2 ... 2024-06-22 11:20:53 +05:30
Kovid Goyal
f4e22ebe3c Implement toggling of boolean features 2024-06-22 11:20:53 +05:30
Kovid Goyal
349c32f60e Work on UI for features 2024-06-22 11:20:53 +05:30
Kovid Goyal
3f919db0c7 Fix preview rendering when height of previewed font greater than cell height 2024-06-22 11:20:53 +05:30
Kovid Goyal
c4dad85f99 test render function to develop the sample renderer 2024-06-22 11:20:53 +05:30
Kovid Goyal
a164c73389 ... 2024-06-22 11:20:53 +05:30
Kovid Goyal
3e430e1a70 Render font feature list in UI 2024-06-22 11:20:53 +05:30
Kovid Goyal
12314cc33f Add tests to validate feature-from-spec 2024-06-22 11:20:53 +05:30
Kovid Goyal
c3bba2e926 work on passing font features via font specs 2024-06-22 11:20:51 +05:30
Kovid Goyal
a4f67b7424 Get feature human readable names 2024-06-22 11:16:42 +05:30
Kovid Goyal
b217c9acde List of all known OpenType font features 2024-06-22 11:16:42 +05:30
Kovid Goyal
23e777ea9e Code to read features from GSUB/GPOS tables 2024-06-22 11:16:42 +05:30
Kovid Goyal
ecb106e92c revert simde bump 2024-06-22 11:16:42 +05:30
Kovid Goyal
23deaae5e7 more tests 2024-06-22 11:16:42 +05:30
Kovid Goyal
bc6230d90c Centralize FontSpec related code 2024-06-22 11:16:42 +05:30
Kovid Goyal
03f35812eb Fix O(n^2) algorithm 2024-06-22 11:16:42 +05:30
Kovid Goyal
7c0007c1bd Ensure bold face is at least as heavy as regular face when auto selecting 2024-06-22 11:16:42 +05:30
Kovid Goyal
d2d2f6c503 Improve auto selection of variable faces 2024-06-22 11:16:42 +05:30
Kovid Goyal
69fb2e4231 Handle variable fonts like cascadia code that dont have a postfix variation prefix name for some of their faces 2024-06-22 11:16:42 +05:30
Kovid Goyal
94d056ed4f Wire up applying of font config 2024-06-22 11:16:41 +05:30
Kovid Goyal
726f62b948 Refactor config patching code to make it re-useable 2024-06-22 11:16:41 +05:30
Kovid Goyal
7e15839141 More work on choose_fonts 2024-06-22 11:16:41 +05:30
Kovid Goyal
13a6ff25a2 Render preview synchronously to avoid flashing 2024-06-22 11:16:41 +05:30
Kovid Goyal
e050557db7 Get axis clicking working 2024-06-22 11:16:41 +05:30
Kovid Goyal
3e2b3a89ce more work on axis fine tuning 2024-06-22 11:16:41 +05:30
Kovid Goyal
81c30cc5fa Render variable axes 2024-06-22 11:16:41 +05:30
Kovid Goyal
73a6668b17 Generalize code to get variable spec 2024-06-22 11:16:41 +05:30
Kovid Goyal
f8e2dc1eca More work on face fine tune UI 2024-06-22 11:16:41 +05:30
Kovid Goyal
32b8077c89 Make debug printing in backend.py more convenient 2024-06-22 11:16:41 +05:30
Kovid Goyal
b90fede2c1 Fix medium face selection when more than family specified 2024-06-22 11:16:41 +05:30
Kovid Goyal
87d1a97486 Dont log an error when the default famil "monospace" is not found 2024-06-22 11:16:41 +05:30
Kovid Goyal
98450a0605 More work on face fine tuning 2024-06-22 11:16:41 +05:30
Kovid Goyal
29377db94c CoreText: When finding medium face for a family prefer variable font if available 2024-06-22 11:16:41 +05:30
Kovid Goyal
8d8c2d7170 Skip test o older freetype 2024-06-22 11:16:41 +05:30
Kovid Goyal
5be4f7b566 Add a test for dejavu sans mono 2024-06-22 11:16:41 +05:30
Kovid Goyal
ce74706c95 Install needed fonts in CI 2024-06-22 11:16:40 +05:30
Kovid Goyal
8f8da2d2c6 IBM Plex Mono workaround is needed only under fontconfig 2024-06-22 11:16:40 +05:30
Kovid Goyal
540e11fa01 DRYer 2024-06-22 11:16:40 +05:30
Kovid Goyal
a230eb87a1 Get font selection for the cascadia code variable fonts working 2024-06-22 11:16:40 +05:30
Kovid Goyal
4ce7da044f Better scoring for malformed fonts with weird weight ranges 2024-06-22 11:16:40 +05:30
Kovid Goyal
ceccacafa8 Refactor scoring 2024-06-22 11:16:40 +05:30
Kovid Goyal
df80da7cb5 Add more font selection tests 2024-06-22 11:16:40 +05:30
Kovid Goyal
08bffde4ad CoreText: Fix selection of font file with multi-file variant font
Also prefer semibold and use similar scorer as fontconfig
2024-06-22 11:16:40 +05:30
Kovid Goyal
983bf134a1 fontconfig: Prefer semi-bold as bold weight even for system selection 2024-06-22 11:16:40 +05:30
Kovid Goyal
e25c61f781 fontconfig: Lift axes spec to named style 2024-06-22 11:16:40 +05:30
Kovid Goyal
d6026c377b Test for font selection 2024-06-22 11:16:40 +05:30
Kovid Goyal
a766a95de6 ... 2024-06-22 11:16:40 +05:30
Kovid Goyal
36d906d4f5 DRYer 2024-06-22 11:16:40 +05:30
Kovid Goyal
881b72fea7 Fix medium face selection via fontconfig when family has only variable font files 2024-06-22 11:16:40 +05:30
Kovid Goyal
d6a7d4a8a1 Fix face sample rendering 2024-06-22 11:16:40 +05:30
Kovid Goyal
db092eb88d Work on face panel 2024-06-22 11:16:40 +05:30
Kovid Goyal
33df5355a6 Start work on face panel 2024-06-22 11:16:39 +05:30
Kovid Goyal
99e279cbe9 ... 2024-06-22 11:16:39 +05:30
Kovid Goyal
82ffb376b2 Use PSname in faces preview panel 2024-06-22 11:16:39 +05:30
Kovid Goyal
fc72f4961e Transmit metadata about rendered samples 2024-06-22 11:16:39 +05:30
Kovid Goyal
880078cab8 Get rendering of faces panel working 2024-06-22 11:16:39 +05:30
Kovid Goyal
88058c9075 Work on faces panel 2024-06-22 11:16:39 +05:30
Kovid Goyal
ecdea9d4d3 Start work on faces panel 2024-06-22 11:16:39 +05:30
Kovid Goyal
5ace209024 ... 2024-06-22 11:16:39 +05:30
Kovid Goyal
b68aac9f28 Forgot to initialize canvas when rendering sample text onto it 2024-06-22 11:16:39 +05:30
Kovid Goyal
71bbf4ecb9 Fix graphics being freed instead of deleted in draw_screen() 2024-06-22 11:16:39 +05:30
Kovid Goyal
1590462038 Get preview to basically display 2024-06-22 11:16:39 +05:30
Kovid Goyal
1735404a05 Move listing code into its own file 2024-06-22 11:16:39 +05:30
Kovid Goyal
3c18d20d1b Code to get specs from options 2024-06-22 11:16:39 +05:30
Kovid Goyal
e51a1362ca Handle default values not present in variation data under CoreText 2024-06-22 11:16:39 +05:30
Kovid Goyal
5cea5cce81 Use the cache for getting variable data for faces 2024-06-22 11:16:38 +05:30
Kovid Goyal
bb722ed6dc Function to get the named style used by a variable font instance 2024-06-22 11:16:38 +05:30
Kovid Goyal
78e81ec589 Get variable font selection working on coretext 2024-06-22 11:16:38 +05:30
Kovid Goyal
80721e1e63 Fix build on older fontconfig 2024-06-22 11:16:38 +05:30
Kovid Goyal
7186b862f2 Cleanup repr for fontconfig faces 2024-06-22 11:16:38 +05:30
Kovid Goyal
e4d82074ff Implement spec based selection for variable fonts 2024-06-22 11:16:38 +05:30
Kovid Goyal
804ad62a33 Start work on displaying font sampler images 2024-06-22 11:16:38 +05:30
Kovid Goyal
4431cff7fa Use KITTY_PID to find kitty exe when possible 2024-06-22 11:16:38 +05:30
Kovid Goyal
78b77b0d01 Fix crash on CoreText for very large font sizes 2024-06-22 11:16:38 +05:30
Kovid Goyal
2c2fbd4445 Implement rendering of sample text 2024-06-22 11:16:38 +05:30
Kovid Goyal
333e94622e Move the query_terminal implementation to Go 2024-06-22 11:16:38 +05:30
Kovid Goyal
41869d88c2 Work on rendering sample text for a font 2024-06-22 11:16:38 +05:30
Kovid Goyal
65b790df40 Also get the current fg/bg colors to render text with 2024-06-22 11:16:38 +05:30
Kovid Goyal
5d74d210ee Query font size and DPI from terminal 2024-06-22 11:16:38 +05:30
Kovid Goyal
431fc98659 Get query terminal working again
Also return current OS Window's font size
2024-06-22 11:16:38 +05:30
Kovid Goyal
21d3759f62 Report when a family has variable fonts 2024-06-22 11:16:38 +05:30
Kovid Goyal
24dea14795 Fix off by one in hyperlink extent 2024-06-22 11:16:37 +05:30
Kovid Goyal
94628dca21 Use correct pointer shape for hyperlinks 2024-06-22 11:16:37 +05:30
Kovid Goyal
d6016f4246 Get clicking on family names functional 2024-06-22 11:16:37 +05:30
Kovid Goyal
688736c4cf ... 2024-06-22 11:16:37 +05:30
Kovid Goyal
1ce31a339e Make kitty +list-fonts a wrapper around choose font 2024-06-22 11:16:37 +05:30
Kovid Goyal
d86da0616e Wire up the backend 2024-06-22 11:16:37 +05:30
Kovid Goyal
926dfd7ba1 Replace list_fonts with choose-fonts kitten 2024-06-22 11:16:37 +05:30
Kovid Goyal
e1b367e1b3 Use stdlib maps/slices 2024-06-22 11:16:37 +05:30
Kovid Goyal
4998fe66b9 Use RenderLines.InRectangle 2024-06-22 11:16:37 +05:30
Kovid Goyal
7f965eba5f Infrastructure for simple internal hyperlink handling 2024-06-22 11:16:37 +05:30
Kovid Goyal
0b743464fb Work on supporting mouse interactions via simple hyperlinks 2024-06-22 11:16:37 +05:30
Kovid Goyal
ffed63a048 Display all styles from STAT table 2024-06-22 11:16:37 +05:30
Kovid Goyal
96c17b0a67 Work on getting styles from STAT table data 2024-06-22 11:16:37 +05:30
Kovid Goyal
8a0b562f4f Work on listing available styles for a family 2024-06-22 11:16:37 +05:30
Kovid Goyal
198aec84c2 Load font variable data on demand 2024-06-22 11:16:37 +05:30
Kovid Goyal
217ded7b3f ... 2024-06-22 11:16:37 +05:30
Kovid Goyal
7522675553 dont use a thread for I/O with kitten 2024-06-22 11:16:36 +05:30
Kovid Goyal
8a8158f287 get multiple JSON messages working 2024-06-22 11:16:36 +05:30
Kovid Goyal
1646c297b3 List families asynchronously 2024-06-22 11:16:36 +05:30
Kovid Goyal
de9d9fd157 Wire up arrow keys for moving in family list 2024-06-22 11:16:36 +05:30
Kovid Goyal
c2e0ecef13 Wire up searching 2024-06-22 11:16:36 +05:30
Kovid Goyal
6e55949094 Start work on list-fonts kitten 2024-06-22 11:16:36 +05:30
Kovid Goyal
21f824e825 Code to read the STAT OpenType table 2024-06-22 11:16:36 +05:30
Kovid Goyal
086185e409 Needed for typing.NotRequired 2024-06-22 11:16:36 +05:30
Kovid Goyal
e3e7bded06 Refactor font selection code to share more between fontconfig and CoreText 2024-06-22 11:16:36 +05:30
Kovid Goyal
7e232b33be Make CoreText signatures for some font finding methods the same as their equivalents in fontconfig 2024-06-22 11:16:36 +05:30
Kovid Goyal
53be33f14d DRYer 2024-06-22 11:16:36 +05:30
Kovid Goyal
54b50858ec Implement basic support for selecting font variations in fontconfig 2024-06-22 11:16:36 +05:30
Kovid Goyal
46d75ea48f Wire up parsing of font specs 2024-06-22 11:16:34 +05:30
Kovid Goyal
bf73720805 Output resolved fonts in debug config 2024-06-22 11:13:23 +05:30
Kovid Goyal
eb580ed91b Implement parsing of fvar table
Cant rely on CoreText for this as it has incomplete and buggy APIs
2024-06-22 11:13:22 +05:30
Kovid Goyal
7f8ce387be Implement postscript variation name prefix for CoreText as well 2024-06-22 11:13:22 +05:30
Kovid Goyal
8ddd20770b Show the postscript variations name prefix when listing fonts 2024-06-22 11:13:22 +05:30
Kovid Goyal
53ea650c27 Use the full name from CoreText when available 2024-06-22 11:13:22 +05:30
Kovid Goyal
3b84498af9 Prune listings of variable fonts
Show only one entry per style per variable font as identified by path
2024-06-22 11:13:22 +05:30
Kovid Goyal
fa52066156 Ensure fontconfig pattern dict has all keys with default values 2024-06-22 11:13:22 +05:30
Kovid Goyal
e2e33cc11c Cleanup resource management
Also add slant to CTFont descriptor
2024-06-22 11:13:22 +05:30
Kovid Goyal
71a8801a68 Output psname for variation font 2024-06-22 11:13:22 +05:30
Kovid Goyal
e54e17acf8 Get variable font data from CoreText 2024-06-22 11:13:22 +05:30
Kovid Goyal
b57cb95522 Start work on listing variable fonts under macOS 2024-06-22 11:13:22 +05:30
Kovid Goyal
12fd1472f5 Fix building on older FreeType 2024-06-22 11:13:22 +05:30
Kovid Goyal
5a86960acd Add information about variable fonts to list-fonts output 2024-06-22 11:13:22 +05:30
Kovid Goyal
dbbc0ea9d4 Code to get variable data from freetype to python 2024-06-22 11:13:22 +05:30
Kovid Goyal
39f1ff6ead Work on list variable fonts on Linux 2024-06-22 11:13:22 +05:30
Kovid Goyal
2d088889f7 Allow fc-list to return variable fonts 2024-06-22 11:13:22 +05:30
Kovid Goyal
48a0a7fe82 Drop support for 32-bit x86 prebuilt binaries
SIMDe 0.8.2 doesnt build on 32 bit and while that will likely be fixed
eventually, 32bit isn't tested in CI and generally speaking there isn't
much use for this platform anymore. I dont know of any 32-bit computers
in common use these days.

As such the overhead of maintaining these is not worth it for me.
kitty itself remains buildable on 32-bit though no guarantees for how
long that will last. kitten remains available on 32bit.
2024-06-22 11:13:22 +05:30
Kovid Goyal
fbeead1661 bump simde to 0.8.2 2024-06-22 11:13:21 +05:30
303 changed files with 6331 additions and 20977 deletions

View File

@@ -15,7 +15,6 @@ 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_macos = 'darwin' in sys.platform.lower()
SW = ''
@@ -67,17 +66,7 @@ def install_fonts() -> None:
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:
try:
tf.extractall(fonts_dir, filter='fully_trusted')
except TypeError:
tf.extractall(fonts_dir)
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')
except TypeError:
tf.extractall(fonts_dir)
tf.extractall(fonts_dir)
def install_deps() -> None:
@@ -161,10 +150,7 @@ def install_bundle() -> None:
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')
except TypeError:
tf.extractall()
tf.extractall()
if not is_macos:
replaced = 0
for dirpath, dirnames, filenames in os.walk('.'):

View File

@@ -23,7 +23,7 @@ jobs:
cc: [gcc, clang]
include:
- python: a
pyver: "3.9"
pyver: "3.8"
sanitize: 0
- python: b
@@ -31,7 +31,7 @@ jobs:
sanitize: 1
- python: c
pyver: "3.11"
pyver: "3.9"
sanitize: 1
@@ -82,7 +82,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.12"
python-version: "3.11"
- name: Install Go
uses: actions/setup-go@v4
@@ -188,29 +188,3 @@ jobs:
- name: Build kitty package
run: python3 .github/workflows/ci.py package
- name: Run benchmarks
run: ./benchmark.py
linux-dev:
name: Test ./dev.sh and benchmark
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v3
with:
fetch-depth: 10
- name: Install build deps
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@v4
with:
go-version-file: go.mod
- name: Build kitty
run: ./dev.sh build
- name: Run benchmarks
run: ./benchmark.py

1
.gitignore vendored
View File

@@ -12,7 +12,6 @@
/dependencies
/tags
/build/
/fonts/
/linux-package/
/kitty.app/
/glad/out/

1140
3rdparty/uthash.h vendored Normal file

File diff suppressed because it is too large Load Diff

1946
3rdparty/verstable.h vendored

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
brew "pkg-config"
brew "zlib"
brew "xxhash"
brew "simde"

View File

@@ -11,4 +11,4 @@ https://www.reddit.com/r/KittyTerminal[Reddit community]
Packaging status in various repositories:
image:https://repology.org/badge/vertical-allrepos/kitty-terminal.svg?columns=3&header=kitty["Packaging status", link="https://repology.org/project/kitty-terminal/versions"]
image:https://repology.org/badge/vertical-allrepos/kitty.svg?columns=3&header=kitty["Packaging status", link="https://repology.org/project/kitty/versions"]

View File

@@ -1,90 +0,0 @@
#!./kitty/launcher/kitty +launch
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import fcntl
import io
import os
import select
import signal
import struct
import sys
import termios
import time
from pty import CHILD, fork
from kitty.constants import kitten_exe
from kitty.fast_data_types import Screen, safe_pipe
from kitty.utils import read_screen_size
def run_parsing_benchmark(cell_width: int = 10, cell_height: int = 20, scrollback: int = 20000) -> None:
isatty = sys.stdout.isatty()
if isatty:
sz = read_screen_size()
columns, rows = sz.cols, sz.rows
else:
columns, rows = 80, 25
child_pid, master_fd = fork()
is_child = child_pid == CHILD
argv = [kitten_exe(), '__benchmark__', '--with-scrollback']
if is_child:
while read_screen_size().width != columns * cell_width:
time.sleep(0.01)
signal.pthread_sigmask(signal.SIG_SETMASK, ())
os.execvp(argv[0], argv)
# os.set_blocking(master_fd, False)
x_pixels = columns * cell_width
y_pixels = rows * cell_height
s = struct.pack('HHHH', rows, columns, x_pixels, y_pixels)
fcntl.ioctl(master_fd, termios.TIOCSWINSZ, s)
write_buf = b''
r_pipe, w_pipe = safe_pipe(True)
class ToChild:
def write(self, x: bytes | str) -> None:
nonlocal write_buf
if isinstance(x, str):
x = x.encode()
write_buf += x
os.write(w_pipe, b'1')
screen = Screen(None, rows, columns, scrollback, cell_width, cell_height, 0, ToChild())
def parse_bytes(data: bytes) -> None:
data = memoryview(data)
while data:
dest = screen.test_create_write_buffer()
s = screen.test_commit_write_buffer(data, dest)
data = data[s:]
screen.test_parse_written_data()
while True:
rd, wd, _ = select.select([master_fd, r_pipe], [master_fd] if write_buf else [], [])
if r_pipe in rd:
os.read(r_pipe, 256)
if master_fd in rd:
try:
data = os.read(master_fd, io.DEFAULT_BUFFER_SIZE)
except OSError:
data = b''
if not data:
break
parse_bytes(data)
if master_fd in wd:
n = os.write(master_fd, write_buf)
write_buf = write_buf[n:]
if isatty:
lines: list[str] = []
screen.linebuf.as_ansi(lines.append)
sys.stdout.write(''.join(lines))
else:
sys.stdout.write(str(screen.linebuf))
def main() -> None:
run_parsing_benchmark()
if __name__ == '__main__':
main()

View File

@@ -21,12 +21,10 @@ import (
const (
folder = "dependencies"
fonts_folder = "fonts"
macos_prefix = "/Users/Shared/kitty-build/sw/sw"
macos_python = "python/Python.framework/Versions/Current/bin/python3"
macos_python_framework = "python/Python.framework/Versions/Current/Python"
macos_python_framework_exe = "python/Python.framework/Versions/Current/Resources/Python.app/Contents/MacOS/Python"
NERD_URL = "https://github.com/ryanoasis/nerd-fonts/releases/latest/download/NerdFontsSymbolsOnly.tar.xz"
)
func root_dir() string {
@@ -37,14 +35,6 @@ func root_dir() string {
return f
}
func fonts_dir() string {
f, e := filepath.Abs(fonts_folder)
if e != nil {
exit(e)
}
return f
}
var _ = fmt.Print
func exit(x any) {
@@ -335,19 +325,6 @@ func dependencies(args []string) {
}); err != nil {
exit(err)
}
tarfile, _ = filepath.Abs(cached_download(NERD_URL))
root = fonts_dir()
if err := os.MkdirAll(root, 0o755); err != nil {
exit(err)
}
cmd = exec.Command("tar", "xf", tarfile, "SymbolsNerdFontMono-Regular.ttf")
cmd.Dir = root
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = cmd.Run(); err != nil {
exit(err)
}
fmt.Println(`Dependencies downloaded. Now build kitty with: ./dev.sh build`)
}

View File

@@ -10,12 +10,16 @@ import subprocess
import tarfile
import time
from bypy.constants import OUTPUT_DIR, PREFIX, python_major_minor_version
from bypy.constants import OUTPUT_DIR, PREFIX, is64bit, 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
j = os.path.join
machine = (os.uname()[4] or '').lower()
if machine.startswith('arm64') or machine.startswith('aarch64'):
arch = 'arm64'
else:
arch = 'x86_64' if is64bit else 'i686'
self_dir = os.path.dirname(os.path.abspath(__file__))
py_ver = '.'.join(map(str, python_major_minor_version()))
iv = globals()['init_env']
@@ -194,13 +198,12 @@ def strip_binaries(files):
def create_tarfile(env, compression_level='9'):
print('Creating archive...')
base = OUTPUT_DIR
arch = 'arm64' if 'arm64' in os.environ['BYPY_ARCH'] else ('i686' if 'i386' in os.environ['BYPY_ARCH'] else 'x86_64')
try:
shutil.rmtree(base)
except OSError as err:
if err.errno not in (errno.ENOENT, errno.EBUSY): # EBUSY when the directory is mountpoint
if err.errno != errno.ENOENT:
raise
os.makedirs(base, exist_ok=True)
os.mkdir(base)
dist = os.path.join(base, f'{kitty_constants["appname"]}-{kitty_constants["version"]}-{arch}.tar')
with tarfile.open(dist, mode='w', format=tarfile.PAX_FORMAT) as tf:
cwd = os.getcwd()
@@ -213,8 +216,7 @@ def create_tarfile(env, compression_level='9'):
print('Compressing archive...')
ans = f'{dist.rpartition(".")[0]}.txz'
start_time = time.time()
threads = 4 if arch == 'i686' else 0
subprocess.check_call(['xz', '--verbose', f'--threads={threads}', '-f', f'-{compression_level}', dist])
subprocess.check_call(['xz', '--verbose', '--threads=0', '-f', f'-{compression_level}', dist])
secs = time.time() - start_time
print('Compressed in {} minutes {} seconds'.format(secs // 60, secs % 60))
os.rename(f'{dist}.xz', ans)

View File

@@ -1,8 +1,8 @@
# Requires installation of XCode >= 10.3 and go 1.23 and Python 3 and
# Requires installation of XCode 10.3 and go 1.19 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 '11.0'
deploy_target '10.14'

View File

@@ -130,7 +130,7 @@ def sign_app(app_dir, notarize):
with make_certificate_useable():
do_sign(app_dir)
if notarize:
notarize_app(app_dir, 'kitty')
notarize_app(app_dir)
class Freeze(object):
@@ -441,7 +441,7 @@ class Freeze(object):
py_compile(join(self.resources_dir, 'Python'))
@flush
def makedmg(self, d, volname, format='ULMO'):
def makedmg(self, d, volname, format='ULFO'):
''' Copy a directory d into a dmg named volname '''
print('\nMaking dmg...')
sys.stdout.flush()

View File

@@ -40,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`

View File

@@ -99,12 +99,12 @@ Customizing the installation
_kitty_install_cmd \
installer=nightly dest=/some/other/location
* You can specify a specific version to install, with:
* You can specify a different install location, with ``dest``:
.. code-block:: sh
_kitty_install_cmd \
installer=version-0.35.2
dest=/some/other/location
* You can tell the installer not to launch |kitty| after installing it with
``launch=n``:

View File

@@ -23,10 +23,7 @@ That's it, kitty will be built from source, magically. You can run it as
This works, because the :code:`./dev.sh build` command downloads all the major
dependencies of kitty as pre-built binaries for your platform and builds kitty
to use these rather than system libraries. The few required system libraries
are mostly X11 and DBUS on Linux, as can be seen in the `linux-dev
<https://github.com/kovidgoyal/kitty/blob/master/.github/workflows/ci.yml>`__
CI job.
to use these rather than system libraries.
If you make changes to kitty code, simply re-run :code:`./dev.sh build`
to build kitty with your changes.
@@ -103,7 +100,6 @@ Build-time dependencies:
* ``simde``
* ``go`` >= _build_go_version (see :file:`go.mod` for go packages used during building)
* ``pkg-config``
* Symbols NERD Font Mono either installed system-wide or placed in :file:`fonts/SymbolsNerdFontMono-Regular.ttf`
* For building on Linux in addition to the above dependencies you might also
need to install the following packages, if they are not already installed by
your distro:

View File

@@ -9,40 +9,6 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
Recent major new features
---------------------------
Cursor trails [0.37]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Show an animated trail when the text cursor makes large jumps making it easy
to follow cursor movements. Inspired by the similar feature in neovide, but
works with terminal multiplexers and kitty windows as well. See :pull:`the pull
request <7970>` for a demonstration video. This feature is optional and must be
turned on by the :opt:`cursor_trail` option in :file:`kitty.conf`.
Variable font support [0.36]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Terminal aficionados spend all day staring at text, so getting text
rendering just right is very important. In that spirit, kitty now supports
`OpenType Variable fonts <https://en.wikipedia.org/wiki/Variable_font>`__.
These allow precise customisation of font characteristics, such as weight and
spacing. Not only that, kitty now has a new :doc:`choose-fonts
<kittens/choose-fonts>` kitten that provides a UI for choosing fonts with
support for font features, variable fonts and previews of how the font will
look. This is in addition to its existing best-in-class font customization
abilities, such as: :opt:`symbol_map`, :opt:`text_composition_strategy`,
:opt:`font_features` and :opt:`modify_font`. kitty knows text rendering is
important, and goes the extra mile for it.
Desktop notifications [0.36]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|kitty| now has a :doc:`notify </kittens/notify>` kitten that can be used to
display desktop notifications from the command line, even over SSH. It has
support for icons, buttons, updating notifications, waiting till
the notification is closed, etc. The underlying :doc:`desktop-notifications`
protocol has been expanded to support all these features.
Wayland goodies [0.34]
~~~~~~~~~~~~~~~~~~~~~~~
@@ -84,207 +50,6 @@ consumption to do the same tasks.
Detailed list of changes
-------------------------------------
0.38.1 [2024-12-26]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- macOS: Fix a regression in the previous release that broke rendering of Emoji using the VS16 variation selector (:iss:`8130`)
- When automatically changing colors based on OS color preference, first reset
all colors to default before applying the new theme so that even colors not
specified in the theme are correct (:iss:`8124`)
- Graphics: Fix deleted but not freed images without any placements being incorrectly freed on a subsequent delete command (:disc:`8129`)
- Graphics: Fix deletion of images by id not working for images with no placements (:disc:`8129`)
- Add support for `escape code protocol <https://github.com/contour-terminal/contour/blob/master/docs/vt-extensions/color-palette-update-notifications.md>`__ for notifying applications on dark/light color scheme change
- Cursor trails: Fix pure vertical movement sometimes not triggering a trail and holding down a key in nvim causing the trail to be glitchy (:pull:`8152`, :pull:`8153`)
- macOS: Fix mouse cursor shape not always being reset to text cursor when mouse re-enters kitty (:iss:`8155`)
- clone-in-kitty: Fix :envvar:`KITTY_WINDOW_ID` being cloned and thus having incorrect value (:iss:`8161`)
0.38.0 [2024-12-15]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Allow :ref:`specifying individual color themes <auto_color_scheme>` to use so that kitty changes colors automatically following the OS dark/light mode
- :opt:`notify_on_cmd_finish`: Automatically remove notifications when the window gains focus or the next notification is shown. Clearing behavior can be configured (:pull:`8100`)
- Discard OSC 9 notifications that start with :code:`4;` because some misguided software is using it for "progress reporting" (:iss:`8011`)
- Wayland GNOME: Workaround bug in mutter causing double tap on titlebar to not always work (:iss:`8054`)
- clipboard kitten: Fix a bug causing kitten to hang in filter mode when input data size is not divisible by 3 and larger than 8KB (:iss:`8059`)
- Wayland: Fix an abort when a client program tries to set an invalid title containing interleaved escape codes and UTF-8 multi-byte characters (:iss:`8067`)
- Graphics protocol: Fix delete by number not deleting newest image with the specified number (:iss:`8071`)
- Fix dashed and dotted underlines not being drawn at the same y position as straight underlines at all font sizes (:iss:`8074`)
- panel kitten: Allow creating floating and on-top panels with arbitrary placement and size on Wayland (:pull:`8068`)
- :opt:`remote_control_password`: Fix using a password without any actions not working (:iss:`8082`)
- Fix enlarging window when a long line is wrapped between the first line of the scrollback buffer and the screen inserting a spurious newline (:iss:`7033`)
- When re-attaching a detached tab preserve internal layout state such as biases and orientations (:iss:`8106`)
- hints/unicode_input kittens: Do not lose keypresses that are sent very rapidly via an automation tool immediately after the kitten is launched (:iss:`7089`)
0.37.0 [2024-10-30]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- A new optional :opt:`text cursor movement animation <cursor_trail>` that
shows a "trail" following the movement of the cursor making it easy to follow
large cursor jumps (:pull:`7970`)
- Custom kittens: Add :ref:`a framework <kitten_main_rc>` for easily and securely using remote control from within a kitten's :code:`main()` function
- kitten icat: Fix the :option:`kitty +kitten icat --no-trailing-newline` not working when using unicode placeholders (:iss:`7948`)
- :opt:`tab_title_template` allow using the 256 terminal colors for formatting (:disc:`7976`)
- Fix resizing window when alternate screen is active does not preserve trailing blank output line in the main screen (:iss:`7978`)
- Wayland: Fix :opt:`background_opacity` less than one causing flicker on startup when the Wayland compositor supports single pixel buffers (:iss:`7987`)
- Fix background image flashing when closing a tab (:iss:`7999`)
- When running a kitten that modifies the kitty config file if no config file exists create a commented out default config file and then modify it (:iss:`7991`)
0.36.4 [2024-09-27]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Fix a regression in the previous release that caused window padding to be rendered opaque even when :opt:`background_opacity` is less than 1 (:iss:`7895`)
- Wayland GNOME: Fix a crash when using multiple monitors with different scales and starting on or moving to the monitor with lower scale (:iss:`7894`)
- macOS: Fix a regression in the previous release that caused junk to be rendered in font previews in the choose fonts kitten and crash on Intel macs (:iss:`7892`)
0.36.3 [2024-09-25]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- The option ``second_transparent_bg`` has been removed and replaced by :opt:`transparent_background_colors` which allows setting up to seven additional colors that will be transparent, with individual opacities per color (:iss:`7646`)
- Fix a regression in the previous release that broke use of the ``cd`` command in session files (:iss:`7829`)
- macOS: Fix shortcuts that become entries in the global menubar being reported as removed shortcuts in the debug output
- macOS: Fix :opt:`macos_option_as_alt` not working when :kbd:`caps lock` is engaged (:iss:`7836`)
- Fix a regression when tinting of background images was introduced that caused window borders to have :opt:`background_opacity` applied to them (:iss:`7850`)
- Fix a regression that broke writing to the clipboard using the OSC 5522 protocol (:iss:`7858`)
- macOS: Fix a regression in the previous release that caused kitty to fail to run after an unclean shutdown/crash when using --single-instance (:iss:`7846`)
- kitten @ ls: Fix the ``--self`` flag not working (:iss:`7864`)
- Remote control: Fix ``--match state:self`` not working (:disc:`7886`)
- Splits layout: Allow setting the ``split_axis`` option to ``auto`` so that all new windows have their split axis chosen automatically unless explicitly specified in the launch command (:iss:`7887`)
0.36.2 [2024-09-06]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Linux: Fix a regression in 0.36.0 that caused font features defined via fontconfig to be ignored (:iss:`7773`)
- :ac:`goto_tab`: Allow numbers less than ``-1`` to go to the Nth previously active tab
- Wayland: Fix for upcoming explicit sync changes in Wayland compositors breaking kitty (:iss:`7767`)
- Remote control: When listening on a UNIX domain socket only allow connections from processes having the same user id (:pull:`7777`)
- kitten @: Fix a regression connecting to TCP sockets using plain IP addresses rather than hostnames (:iss:`7794`)
- diff kitten: Fix a regression that broke diffing against remote files (:iss:`7797`)
0.36.1 [2024-08-24]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Allow specifying that the :opt:`cursor shape for unfocused windows <cursor_shape_unfocused>` should remain unchanged (:pull:`7728`)
- MacOS Intel: Fix a crash in the choose-fonts kitten when displaying previews of variable fonts (:iss:`7734`)
- Remote control: Fix a regression causing an escape code to leak when using @ launch with ``--no-response`` over the TTY (:iss:`7752`)
- OSC 52: Fix a regression in the previous release that broke handling of invalid base64 encoded data in OSC 52 requests (:iss:`7757`)
- macOS: Fix a regression in the previous release that caused :option:`kitty --single-instance` to not work when using :file:`macos-launch-services-cmdline`
0.36.0 [2024-08-17]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Support `OpenType Variable fonts <https://en.wikipedia.org/wiki/Variable_font>`__ (:iss:`3711`)
- A new :doc:`choose-fonts </kittens/choose-fonts>` kitten that provides a UI with font previews to ease selection of fonts. Also has support for font features and variable fonts
- Allow animating the blinking of the cursor. See :opt:`cursor_blink_interval` for how to configure it
- Add NERD fonts builtin so that users don't have to install them to use NERD symbols in kitty. The builtin font is used only if the symbols are not available in some system font
- launch command: A new :option:`launch --bias` option to adjust the size of newly created windows declaratively (:iss:`7634`)
- A new option :opt:`transparent_background_colors` to make a second background color semi-transparent via :opt:`background_opacity`. Useful for things like cursor line highlight in editors (:iss:`7646`)
- A new :doc:`notify </kittens/notify>` kitten to show desktop notifications
from the command line with support for icons, buttons and more.
- Desktop notifications protocol: Add support for icons, buttons, closing of notifications, expiry of notifications, updating of notifications and querying if the terminal emulator supports the protocol (:iss:`7657`, :iss:`7658`, :iss:`7659`)
- A new option :opt:`filter_notification` to filter out or perform arbitrary actions on desktop notifications based on sophisticated criteria (:iss:`7670`)
- A new protocol to allow terminal applications to change colors in the terminal more robustly than with the legacy XTerm protocol (:ref:`color_control`)
- Sessions: A new command ``focus_matching_window`` to shift focus to a specific window, useful when creating complex layouts with splits (:disc:`7635`)
- Speed up loading of large background images by caching the decoded image data. Also allow using images in JPEG/WEBP/TIFF/GIF/BMP formats in addition to PNG
- Wayland: Allow fractional scales less than one (:pull:`7549`)
- Wayland: Fix specifying the output name for the panel kitten not working (:iss:`7573`)
- icat kitten: Add an option :option:`kitty +kitten icat --no-trailing-newline` to leave the cursor to the right of the image (:iss:`7574`)
- Speed up ``kitty --version`` and ``kitty --single-instance`` (for all subsequent instances). They are now the fastest of all terminal emulators with similar functionality
- macOS: Fix rendering of the unicode hyphen (U+2010) character when using a font that does not include a glyph for it (:iss:`7525`)
- macOS 15: Handle Fn modifier when detecting global shortcuts (:iss:`7582`)
- Dispatch any clicks waiting for :opt:`click_interval` on key events (:iss:`7601`)
- ``kitten run-shell``: Automatically add the directory containing the kitten binary to PATH if needed. Controlled via the ``--inject-self-onto-path`` option (`disc`:7668`)
- Wayland: Fix an issue with mouse selections not being stopped when there are multiple OS windows (:iss:`7381`)
- Splits layout: Fix the ``move_to_screen_edge`` action breaking when only a single window is present (:iss:`7621`)
- Add support for in-band window resize notifications (:iss:`7642`)
- Allow controlling the easing curves used for :opt:`visual_bell_duration`
- New special rendering for font symbols useful in drawing commit graphs (:pull:`7681`)
- diff kitten: Add bindings to jump to next and previous file (:pull:`7683`)
- Wayland GNOME: Fix the font size in the OS Window title bar changing with the size of the text in the window (:disc:`7677`)
- Wayland GNOME: Fix a small rendering artifact when docking a window at a screen edge or maximizing it (:iss:`7701`)
- When :opt:`shell` is set to ``.`` respect the SHELL environment variable in the environment in which kitty is launched (:pull:`7714`)
- macOS: Bump the minimum required macOS version to Catalina released five years ago.
- Fix a regression in :opt:`notify_on_cmd_finish` that caused notifications to appear for every command after the first (:iss:`7725`)
0.35.2 [2024-06-22]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -1,8 +1,5 @@
Color control
====================
Saving and restoring colors
------------------------------
==============================
It is often useful for a full screen application with its own color themes to
set the default foreground, background, selection and cursor colors and the ANSI
@@ -27,157 +24,3 @@ foreground, selection background, selection foreground and cursor color and the
promoting interoperability, kitty added support for xterm's escape codes as
well, and changed this extension to also save/restore the entire ANSI color
table.
.. _color_control:
Setting and querying colors
-------------------------------
While there exists a legacy protocol developed by XTerm for querying and
setting colors, as with most XTerm protocols it suffers from the usual design
limitations of being under specified and in-sufficient. XTerm implements
querying of colors using OSC 4,5,6,10-19,104,105,106,110-119. This absurd
profusion of numbers is completely unnecessary, redundant and requires adding
two new numbers for every new color. Also XTerm's protocol doesn't handle the
case of colors that are unknown to the terminal or that are not a set value,
for example, many terminals implement selection as a reverse video effect not a
fixed color. The XTerm protocol has no way to query for this condition. The
protocol also doesn't actually specify the format in which colors are reported,
deferring to a man page for X11!
Instead kitty has developed a single number based protocol that addresses all
these shortcomings and is future proof by virtue of using string keys rather
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 ``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
special colors:
================================= =============================================== ===============================
key meaning dynamic
================================= =============================================== ===============================
foreground The default foreground text color Not applicable
background The default background text color Not applicable
selection_background The background color of selections Reverse video
selection_foreground The foreground color of selections Reverse video
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..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
:opt:`background_opacity` value.
================================= =============================================== ===============================
In this table the third column shows what effect setting the color to *dynamic*
has in kitty and many other terminal emulators. It is advisory only, terminal
emulators may not support dynamic colors for these or they may have other
effects. Setting the ANSI color table colors to dynamic is not allowed.
Querying current color values
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To query colors values, the client program sends this escape code with the
``value`` field set to ``?`` (the byte ``0x3f``). The terminal then responds
with the same escape code, but with the ``?`` replaced by the :ref:`encoded
color value <color_control_color_encoding>`. If the queried color is one that
does not have a defined value, for example, if the terminal is using a reverse
video effect or a gradient or similar, then the value must be empty, that is
the response contains only the key and ``=``, no value. For example, if the
client sends::
<OSC> 21 ; foreground=? ; cursor=? <ST>
The terminal responds::
<OSC> 21 ; foreground=rgb:ff/00/00 ; cursor= <ST>
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 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.
Setting color values
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To set a color value, the client program sends this escape code with the
``value`` field set to either an :ref:`encoded color value
<color_control_color_encoding>` or the empty value. The empty value means
the terminal should use a dynamic color for example reverse video for
selections or similar. To reset a color to its default value (i.e. the value it
would have if it was never set) the client program should send just the key
name with no ``=`` and no value. For example::
<OSC> 21 ; foreground=green ; cursor= ; background <ST>
This sets the foreground to the color green, sets the cursor color to dynamic
(usually meaning the cursor takes the color of the text under it) and resets
the background color to its default value.
To check if setting succeeded, the client can simply query the color, in fact
the two can be combined into a single escape code, for example::
<OSC> 21 ; foreground=white ; foreground=? <ST>
The terminal will change the foreground color and reply with the new foreground
color.
.. _color_control_color_encoding:
Color value encoding
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The color encoding is inherited from the scheme used by XTerm, for
compatibility, but a sane, rigorously specified subset is chosen.
RGB colors are encoded in one of three forms:
``rgb:<red>/<green>/<blue>``
| <red>, <green>, <blue> := h | hh | hhh | hhhh
| h := single hexadecimal digits (case insignificant)
| Note that h indicates the value scaled in 4 bits, hh the value scaled in 8 bits,
hhh the value scaled in 12 bits, and hhhh the value scaled in 16 bits, respectively.
``#<h...>``
| h := single hexadecimal digits (case insignificant)
| #RGB (4 bits each)
| #RRGGBB (8 bits each)
| #RRRGGGBBB (12 bits each)
| #RRRRGGGGBBBB (16 bits each)
| The R, G, and B represent single hexadecimal digits. When fewer than 16 bits
each are specified, they represent the most significant bits of the value
(unlike the “rgb:” syntax, in which values are scaled). For example,
the string ``#3a7`` is the same as ``#3000a0007000``.
``rgbi:<red>/<green>/<blue>``
red, green, and blue are floating-point values between 0.0 and 1.0, inclusive. The input format for these values is an optional
sign, a string of numbers possibly containing a decimal point, and an optional exponent field containing an E or e followed by a possibly
signed integer string. Values outside the ``0 - 1`` range must be clipped to be within the range.
If a color should have an alpha component, it must be suffixed to the color
specification in the form :code:`@number between zero and one`. For example::
red@0.5 rgb:ff0000@0.1 #ff0000@0.3
The syntax for the floating point alpha component is the same as used for the
components of ``rgbi`` defined above. When not specified, the default alpha
value is ``1.0``. Values outside the range ``0 - 1`` must be clipped
to be within the range, negative values may have special context dependent
meaning.
In addition, the following color names are accepted (case-insensitively) corresponding to the
specified RGB values.
.. include:: generated/color-names.rst

View File

@@ -282,29 +282,13 @@ if you specify a program-to-run you can use the special placeholder
p(f'\nThe source code for this kitten is `available on GitHub <{scurl}>`_.')
p('\nCommand Line Interface')
p('-' * 72)
appname = f'kitten {kitten}'
if kitten in ('panel', 'broadcast', 'remote_file'):
appname = 'kitty +' + appname
p('\n\n' + option_spec_as_rst(
data['options'], message=data['help_text'], usage=data['usage'], appname=appname, heading_char='^'))
data['options'], message=data['help_text'], usage=data['usage'], appname=f'kitty +kitten {kitten}',
heading_char='^'))
# }}}
def write_color_names_table() -> None: # {{{
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:
p = partial(print, file=f)
p('=' * 50, '=' * 20)
p('Name'.ljust(50), 'RGB value')
p('=' * 50, '=' * 20)
for name, col in color_names.items():
p(name.ljust(50), s(col))
p('=' * 50, '=' * 20)
# }}}
def write_remote_control_protocol_docs() -> None: # {{{
from kitty.rc.base import RemoteCommand, all_command_names, command_for_name
field_pat = re.compile(r'\s*([^:]+?)\s*:\s*(.+)')
@@ -333,10 +317,8 @@ def write_remote_control_protocol_docs() -> None: # {{{
else:
title = f'{title} (optional)'
p(f':code:`{title}`')
p(' ', desc)
p()
p()
p()
p(' ', desc), p()
p(), p()
with open('generated/rc.rst', 'w') as f:
p = partial(print, file=f)
@@ -719,7 +701,7 @@ def setup_man_pages() -> None:
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
for x in glob.glob(os.path.join(base, 'docs/kittens/*.rst')):
kn = os.path.basename(x).rpartition('.')[0]
if kn in ('custom', 'developing-builtin-kittens'):
if kn == 'custom':
continue
cd = get_kitten_cli_docs(kn) or {}
khn = kn.replace('_', '-')
@@ -766,7 +748,6 @@ def setup(app: Any) -> None:
kn = all_kitten_names()
write_cli_docs(kn)
write_remote_control_protocol_docs()
write_color_names_table()
write_conf_docs(app, kn)
app.add_config_value('string_replacements', {}, True)
app.connect('source-read', replace_string)

View File

@@ -33,16 +33,7 @@ notification from a shell script::
To show a message with a title and a body::
printf '\x1b]99;i=1:d=0;Hello world\x1b\\'
printf '\x1b]99;i=1:p=body;This is cool\x1b\\'
.. tip::
|kitty| also comes with its own :doc:`statically compiled command line tool </kittens/notify>` to easily display
notifications, with all their advanced features. For example:
.. code-block:: sh
kitten notify "Hello world" A good day to you
printf '\x1b]99;i=1:d=1:p=body;This is cool\x1b\\'
The most important key in the metadata is the ``p`` key, it controls how the
payload is interpreted. A value of ``title`` means the payload is setting the
@@ -52,55 +43,20 @@ and so on, see the table below for full details.
The design of the escape code is fundamentally chunked, this is because
different terminal emulators have different limits on how large a single escape
code can be. Chunking is accomplished by the ``i`` and ``d`` keys. The ``i``
key is the *notification id* which is an :ref:`identifier`.
The ``d`` key stands for *done* and can only take the
key is the *notification id* which can be any string containing the characters
``[a-zA-Z0-9_-+.]``. The ``d`` key stands for *done* and can only take the
values ``0`` and ``1``. A value of ``0`` means the notification is not yet done
and the terminal emulator should hold off displaying it. A non-zero value means
and the terminal emulator should hold off displaying it. A value of ``1`` means
the notification is done, and should be displayed. You can specify the title or
body multiple times and the terminal emulator will concatenate them, thereby
allowing arbitrarily long text (terminal emulators are free to impose a sensible
limit to avoid Denial-of-Service attacks). The size of the payload must be no
longer than ``2048`` bytes, *before being encoded* or ``4096`` encoded bytes.
longer than ``2048`` bytes, *before being encoded*.
Both the ``title`` and ``body`` payloads must be either :ref:`safe_utf8` text
or UTF-8 text that is :ref:`base64` encoded, in which case there must be an
``e=1`` key in the metadata to indicate the payload is :ref:`base64`
encoded. No HTML or other markup in the plain text is allowed. It is strictly
plain text, to be interpreted as such.
Allowing users to filter notifications
-------------------------------------------------------
.. versionadded:: 0.36.0
Specifying application name and notification type
Well behaved applications should identify themselves to the terminal
by means of two keys ``f`` which is the application name and ``t``
which is the notification type. These are free form keys, they can contain
any values, their purpose is to allow users to easily filter out
notifications they do not want. Both keys must have :ref:`base64`
encoded UTF-8 text as their values. The ``t`` key can be specified multiple
times, as notifications can have more than one type. See the `freedesktop.org
spec
<https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html#categories>`__
for examples of notification types.
.. note::
The application name should generally be set to the filename of the
applications `desktop file
<https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#file-naming>`__
(without the ``.desktop`` part) or the bundle identifier for a macOS
application. While not strictly necessary, this allows the terminal
emulator to deduce an icon for the notification when one is not specified.
.. tip::
|kitty| has sophisticated notification filtering and management
capabilities via :opt:`filter_notification`.
Being informed when user activates the notification
-------------------------------------------------------
Both the ``title`` and ``body`` payloads must be either UTF-8 encoded plain
text with no embedded escape codes, or UTF-8 text that is Base64 encoded, in
which case there must be an ``e=1`` key in the metadata to indicate the payload
is Base64 encoded.
When the user clicks the notification, a couple of things can happen, the
terminal emulator can focus the window from which the notification came, and/or
@@ -115,319 +71,29 @@ escape code is::
The value of ``identifier`` comes from the ``i`` key in the escape code sent by
the application. If the application sends no identifier, then the terminal
*must* use ``i=0``. (Ideally ``i`` should have been left out from the response,
but for backwards compatibility ``i=0`` is used). Actions can be preceded by a
negative sign to turn them off, so for example if you do not want any action,
turn off the default ``focus`` action with::
*must* use ``i=0``. Actions can be preceded by a negative sign to turn them
off, so for example if you do not want any action, turn off the default
``focus`` action with::
a=-focus
Complete specification of all the metadata keys is in the :ref:`table below <keys_in_notificatons_protocol>`.
If a terminal emulator encounters a key in the metadata it does not understand,
Complete specification of all the metadata keys is in the table below. If a
terminal emulator encounters a key in the metadata it does not understand,
the key *must* be ignored, to allow for future extensibility of this escape
code. Similarly if values for known keys are unknown, the terminal emulator
*should* either ignore the entire escape code or perform a best guess effort to
display it based on what it does understand.
Being informed when a notification is closed
------------------------------------------------
.. versionadded:: 0.36.0
Notifications of close events
If you wish to be informed when a notification is closed, you can specify
``c=1`` when sending the notification. For example::
<OSC> 99 ; i=mynotification : c=1 ; hello world <terminator>
Then, the terminal will send the following
escape code to inform when the notification is closed::
<OSC> 99 ; i=mynotification : p=close ; <terminator>
If no notification id was specified ``i=0`` will be used in the response
If ``a=report`` is specified and the notification is activated/clicked on
then both the activation report and close notification are sent. If the notification
is updated then the close event is not sent unless the updated notification
also requests a close notification.
Note that on some platforms, such as macOS, the OS does not inform applications
when notifications are closed, on such platforms, terminals reply with::
<OSC> 99 ; i=mynotification : p=close ; untracked <terminator>
This means that the terminal has no way of knowing when the notification is
closed. Instead, applications can poll the terminal to determine which
notifications are still alive (not closed), with::
<OSC> 99 ; i=myid : p=alive ; <terminator>
The terminal will reply with::
<OSC> 99 ; i=myid : p=alive ; id1,id2,id3 <terminator>
Here, ``myid`` is present for multiplexer support. The response from the terminal
contains a comma separated list of ids that are still alive.
Updating or closing an existing notification
----------------------------------------------
.. versionadded:: 0.36.0
The ability to update and close a previous notification
To update a previous notification simply send a new notification with the same
*notification id* (``i`` key) as the one you want to update. If the original
notification is still displayed it will be replaced, otherwise a new
notification is displayed. This can be used, for example, to show progress of
an operation. How smoothly the existing notification is replaced
depends on the underlying OS, for example, on Linux the replacement is usually flicker
free, on macOS it isn't, because of Apple's design choices.
Note that if no ``i`` key is specified, no updating must take place, even if
there is a previous notification without an identifier. The terminal must
treat these as being two unique *unidentified* notifications.
To close a previous notification, send::
<OSC> i=<notification id> : p=close ; <terminator>
This will close a previous notification with the specified id. If no such
notification exists (perhaps because it was already closed or it was activated)
then the request is ignored. If no ``i`` key is specified, this must be a no-op.
Automatically expiring notifications
-------------------------------------
A notification can be marked as expiring (being closed) automatically after
a specified number of milliseconds using the ``w`` key. The default if
unspecified is ``-1`` which means to use whatever expiry policy the OS has for
notifications. A value of ``0`` means the notification should never expire.
Values greater than zero specify the number of milliseconds after which the
notification should be auto-closed. Note that the value of ``0``
is best effort, some platforms honor it and some do not. Positive values
are robust, since they can be implemented by the terminal emulator itself,
by manually closing the notification after the expiry time. The notification
could still be closed before the expiry time by user interaction or OS policy,
but it is guaranteed to be closed once the expiry time has passed.
Adding icons to notifications
--------------------------------
.. versionadded:: 0.36.0
Custom icons in notifications
Applications can specify a custom icon to be displayed with a notification.
This can be the application's logo or a symbol such as error or warning
symbols. The simplest way to specify an icon is by *name*, using the ``n``
key. The value of this key is :ref:`base64` encoded UTF-8 text. Names
can be either application names, or symbol names. The terminal emulator
will try to resolve the name based on icons and applications available
on the computer it is running on. The following list of well defined names
must be supported by any terminal emulator implementing this spec.
The ``n`` key can be specified multiple times, the terminal will go through
the list in order and use the first icon that it finds available on the
system.
.. table:: Universally available icon names
======================== ==============================================
Name Description
======================== ==============================================
``error`` An error symbol
``warn``, ``warning`` A warning symbol
``info`` A symbol denoting an informational message
``question`` A symbol denoting asking the user a question
``help`` A symbol denoting a help message
``file-manager`` A symbol denoting a generic file manager application
``system-monitor`` A symbol denoting a generic system monitoring/information application
``text-editor`` A symbol denoting a generic text editor application
======================== ==============================================
If an icon name is an application name it should be an application identifier,
such as the filename of the application's :file:`.desktop` file on Linux or its
bundle identifier on macOS. For example if the cross-platform application
FooBar has a desktop file named: :file:`foo-bar.desktop` and a bundle
identifier of ``net.foo-bar-website.foobar`` then it should use the icon names
``net.foo-bar-website.foobar`` *and* ``foo-bar`` so that terminals running on
both platforms can find the application icon.
If no icon is specified, but the ``f`` key (application name) is specified, the
terminal emulator should use the value of the ``f`` key to try to find a
suitable icon.
Adding icons by transmitting icon data
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This can be done by using the ``p=icon`` key. Then, the payload is the icon
image in any of the ``PNG``, ``JPEG`` or ``GIF`` image formats. It is recommended
to use an image size of ``256x256`` for icons. Since icons are binary data,
they must be transmitted encoded, with ``e=1``.
When both an icon name and an image are specified, the terminal emulator must
first try to find a locally available icon matching the name and only if one
is not found, fallback to the provided image. This is so that users are
presented with icons from their current icon theme, where possible.
Transmitted icon data can be cached using the ``g`` key. The value of the ``g``
key must be a random globally unique UUID like :ref:`identifier`. Then, the
terminal emulator will cache the transmitted data using that key. The cache
should exist for as long as the terminal emulator remains running. Thus, in
future notifications, the application can simply send the ``g`` key to display
a previously cached icon image with needing to re-transmit the actual data with
``p=icon``. The ``g`` key refers only to the icon data, multiple different
notifications with different icon or application names can use the same ``g``
key to refer to the same icon. Terminal multiplexers must cache icon data
themselves and refresh it in the underlying terminal implementation when
detaching and then re-attaching. This means that applications once started
need to transmit icon data only once until they are quit.
*should* either ignore the entire escape code or perform a best guess effort
to display it based on what it does understand.
.. note::
To avoid DoS attacks terminal implementations can impose a reasonable max size
on the icon cache and evict icons in order of last used. Thus theoretically,
a previously cached icon may become unavailable, but given that icons are
small images, practically this is not an issue in all but the most resource
constrained environments, and the failure mode is simply that the icon is not
displayed.
It is possible to extend this escape code to allow specifying an icon for
the notification, however, given that some platforms, such as legacy versions
of macOS, don't allow displaying custom images on a notification, it was
decided to leave it out of the spec for the time being.
.. note::
How the icon is displayed depends on the underlying OS notifications
implementation. For example, on Linux, typically a single icon is displayed.
On macOS, both the terminal emulator's icon and the specified custom icon
are displayed.
Similarly, features such as scheduled notifications could be added in future
revisions.
Adding buttons to the notification
---------------------------------------
Buttons can be added to the notification using the *buttons* payload, with ``p=buttons``.
Buttons are a list of UTF-8 text separated by the Unicode Line Separator
character (U+2028) which is the UTF-8 bytes ``0xe2 0x80 0xa8``. They can be
sent either as :ref:`safe_utf8` or :ref:`base64`. When the user clicks on one
of the buttons, and reporting is enabled with ``a=report``, the terminal will
send an escape code of the form::
<OSC> 99 ; i=identifier ; button_number <terminator>
Here, `button_number` is a number from 1 onwards, where 1 corresponds
to the first button, two to the second and so on. If the user activates the
notification as a whole, and not a specific button, the response, as described
above is::
<OSC> 99 ; i=identifier ; <terminator>
If no identifier was specified when creating the notification, ``i=0`` is used.
The terminal *must not* send a response unless report is requested with
``a=report``.
.. note::
The appearance of the buttons depends on the underlying OS implementation.
On most Linux systems, the buttons appear as individual buttons on the
notification. On macOS they appear as a drop down menu that is accessible
when hovering the notification. Generally, using more than two or three
buttons is not a good idea.
.. _notifications_query:
Playing a sound with notifications
-----------------------------------------
.. versionadded:: 0.36.0
The ability to control the sound played with notifications
By default, notifications may or may not have a sound associated with them
depending on the policies of the OS notifications service. Sometimes it
might be useful to ensure a notification is not accompanied by a sound.
This can be done by using the ``s`` key which accepts :ref:`base64` encoded
UTF-8 text as its value. The set of known sounds names is in the table below,
any other names are implementation dependent, for instance, on Linux, terminal emulators will
probably support the `standard sound names
<https://specifications.freedesktop.org/sound-naming-spec/latest/#names>`__
.. table:: Standard sound names
======================== ==============================================
Name Description
======================== ==============================================
``system`` The default system sound for a notification, which may be some kind of beep or just silence
``silent`` No sound must accompany the notification
``error`` A sound associated with error messages
``warn``, ``warning`` A sound associated with warning messages
``info`` A sound associated with information messages
``question`` A sound associated with questions
======================== ==============================================
Support for sound names can be queried as described below.
Querying for support
-------------------------
.. versionadded:: 0.36.0
The ability to query for support
An application can query the terminal emulator for support of this protocol, by
sending the following escape code::
<OSC> 99 ; i=<some identifier> : p=? ; <terminator>
A conforming terminal must respond with an escape code of the form::
<OSC> 99 ; i=<some identifier> : p=? ; key=value : key=value <terminator>
The identifier is present to support terminal multiplexers, so that they know
which window to redirect the query response too.
Here, the ``key=value`` parts specify details about what the terminal
implementation supports. Currently, the following keys are defined:
======= ================================================================================
Key Value
======= ================================================================================
``a`` Comma separated list of actions from the ``a`` key that the terminal
implements. If no actions are supported, the ``a`` key must be absent from the
query response.
``c`` ``c=1`` if the terminal supports close events, otherwise the ``c``
must be omitted.
``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.
``p`` Comma spearated list of supported payload types (i.e. values of the
``p`` key that the terminal implements). These must contain at least
``title``.
``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 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
query response.
``w`` ``w=1`` if the terminal supports auto expiring of notifications.
======= ================================================================================
In the future, if this protocol expands, more keys might be added. Clients must
ignore keys they do not understand in the query response.
To check if a terminal emulator supports this notifications protocol the best way is to
send the above *query action* followed by a request for the `primary device
attributes <https://vt100.net/docs/vt510-rm/DA1.html>`_. If you get back an
answer for the device attributes without getting back an answer for the *query
action* the terminal emulator does not support this notifications protocol.
.. _keys_in_notificatons_protocol:
Specification of all keys used in the protocol
--------------------------------------------------
======= ==================== ========== =================
Key Value Default Description
======= ==================== ========== =================
@@ -437,30 +103,16 @@ Key Value Default Description
optional leading
``-``
``c`` ``0`` or ``1`` ``0`` When non-zero an escape code is sent to the application when the notification is closed.
``d`` ``0`` or ``1`` ``1`` Indicates if the notification is
complete or not. A non-zero value
means it is complete.
complete or not.
``e`` ``0`` or ``1`` ``0`` If set to ``1`` means the payload is :ref:`base64` encoded UTF-8,
``e`` ``0`` or ``1`` ``0`` If set to ``1`` means the payload is Base64 encoded UTF-8,
otherwise it is plain UTF-8 text with no C0 control codes in it
``f`` :ref:`base64` ``unset`` The name of the application sending the notification. Can be used to filter out notifications.
encoded UTF-8
application name
``i`` ``[a-zA-Z0-9-_+.]`` ``0`` Identifier for the notification
``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 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.
``n`` :ref:`base64` ``unset`` Icon name. Can be specified multiple times.
encoded UTF-8
application name
``p`` One of ``title`` or ``title`` Whether the payload is the notification title or body. If a
``body``. notification has no title, the body will be used as title.
``o`` One of ``always``, ``always`` When to honor the notification request. ``unfocused`` means when the window
``unfocused`` or the notification is sent on does not have keyboard focus. ``invisible``
@@ -468,27 +120,8 @@ Key Value Default Description
and not visible to the user, for example, because it is in an inactive tab or
its OS window is not currently active.
``always`` is the default and always honors the request.
``p`` One of ``title``, ``title`` Type of the payload. If a notification has no title, the body will be used as title.
``body``, A notification with not title and no body is ignored. Terminal
``close``, emulators should ignore payloads of unknown type to allow for future
``icon``, expansion of this protocol.
``?``, ``alive``,
``buttons``
``s`` :ref:`base64` ``system`` The sound name to play with the notification. ``silent`` means no sound.
encoded sound ``system`` means to play the default sound, if any, of the platform notification service.
name Other names are implementation dependent.
``t`` :ref:`base64` ``unset`` The type of the notification. Used to filter out notifications. Can be specified multiple times.
encoded UTF-8
notification type
``u`` ``0, 1 or 2`` ``unset`` The *urgency* of the notification. ``0`` is low, ``1`` is normal and ``2`` is critical.
If not specified normal is used.
``w`` ``>=-1`` ``-1`` The number of milliseconds to auto-close the notification after.
======= ==================== ========== =================
@@ -503,49 +136,3 @@ Key Value Default Description
|kitty| also supports the `legacy OSC 9 protocol developed by iTerm2
<https://iterm2.com/documentation-escape-codes.html>`__ for desktop
notifications.
.. _base64:
Base64
---------------
The base64 encoding used in the this specification is the one defined in
:rfc:`4648`. When a base64 payload is chunked, either the chunking should be
done before encoding or after. When the chunking is done before encoding, no
more than 2048 bytes of data should be encoded per chunk and the encoded data
**must** include the base64 padding bytes, if any. When the chunking is done
after encoding, each encoded chunk must be no more than 4096 bytes in size.
There may or may not be padding bytes at the end of the last chunk, terminals
must handle either case.
.. _safe_utf8:
Escape code safe UTF-8
--------------------------
This must be valid UTF-8 as per the spec in :rfc:`3629`. In addition, in order
to make it safe for transmission embedded inside an escape code, it must
contain none of the C0 and C1 control characters, that is, the unicode
characters: U+0000 (NUL) - U+1F (Unit separator), U+7F (DEL) and U+80 (PAD) - U+9F
(APC). Note that in particular, this means that no newlines, carriage returns,
tabs, etc. are allowed.
.. _identifier:
Identifier
----------------
Any string consisting solely of characters from the set ``[a-zA-Z0-9_-+.]``,
that is, the letters ``a-z``, ``A-Z``, the underscore, the hyphen, the plus
sign and the period. Applications should make these globally unique, like a
UUID for maximum robustness.
.. important::
Terminals **must** sanitize ids received from client programs before sending
them back in responses, to mitigate input injection based attacks. That is, they must
either reject ids containing characters not from the above set, or remove
bad characters when reading ids sent to them.

View File

@@ -257,30 +257,27 @@ fonts to be freely resizable, so it does not support bitmapped fonts.
.. note::
If you are trying to use a font patched with `Nerd Fonts
<https://nerdfonts.com/>`__ symbols, don't do that as patching destroys
fonts. There is no need, kitty has a builtin NERD font and will use it for
symbols not found in any other font on your system.
If you have patched fonts on your system they might be used instead for NERD
symbols, so to force kitty to use the pure NERD font for NERD symbols,
add the following line to :file:`kitty.conf`::
fonts. There is no need, simply install the standalone ``Symbols Nerd Font Mono``
(the file :file:`NerdFontsSymbolsOnly.zip` from the `Nerd Fonts releases page
<https://github.com/ryanoasis/nerd-fonts/releases>`__). kitty should pick up
symbols from it automatically, and you can tell it to do so explicitly in
case it doesn't with the :opt:`symbol_map` directive::
# Nerd Fonts v3.2.0
# Nerd Fonts v3.1.0
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+e6b1,U+e700-U+e7c5,U+ed00-U+efc1,U+f000-U+f2ff,U+f000-U+f2e0,U+f300-U+f372,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+e0d4,U+e200-U+e2a9,U+e300-U+e3e3,U+e5fa-U+e6b1,U+e700-U+e7c5,U+f000-U+f2e0,U+f300-U+f372,U+f400-U+f532,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
not included.
If your font is not listed in ``kitten choose-fonts`` it means that it is not
If your font is not listed in ``kitty +list-fonts`` it means that it is not
monospace or is a bitmapped font. On Linux you can list all monospace fonts
with::
fc-list : family spacing outline scalable | grep -e spacing=100 -e spacing=90 | grep -e outline=True | grep -e scalable=True
On macOS, you can open *Font Book* and look in the :guilabel:`Fixed width`
collection to see all monospaced fonts on your system.
Note that **on Linux**, the spacing property is calculated by fontconfig based on actual glyph
Note that the spacing property is calculated by fontconfig based on actual glyph
widths in the font. If for some reason fontconfig concludes your favorite
monospace font does not have ``spacing=100`` you can override it by using the
following :file:`~/.config/fontconfig/fonts.conf`::
@@ -303,7 +300,7 @@ command to rebuild your fontconfig cache::
fc-cache -r
Then, the font will be available in ``kitten choose-fonts``.
Then, the font will be available in ``kitty +list-fonts``.
How can I assign a single global shortcut to bring up the kitty terminal?
@@ -318,9 +315,8 @@ see :iss:`here <45>`.
I do not like the kitty icon!
-------------------------------
The kitty icon was created as tribute to my cat of nine years who passed away,
as such it is not going to change. However, if you do not like it, there are
many alternate icons available, click on an icon to visit its homepage:
There are many alternate icons available, click on an icon to visit its
homepage:
.. image:: https://github.com/k0nserv/kitty-icon/raw/main/kitty.iconset/icon_256x256.png
:target: https://github.com/k0nserv/kitty-icon
@@ -346,10 +342,6 @@ many alternate icons available, click on an icon to visit its homepage:
:target: https://github.com/samholmes/whiskers
:width: 256
.. image:: https://github.com/user-attachments/assets/a37d7830-4a8c-45a8-988a-3e98a41ea541
:target: https://github.com/diegobit/kitty-icon
:width: 256
.. image:: https://github.com/eccentric-j/eccentric-icons/raw/main/icons/kitty-terminal/2d/kitty-preview.png
:target: https://github.com/eccentric-j/eccentric-icons
:width: 256
@@ -403,11 +395,9 @@ This is accomplished by using ``map`` with :ac:`send_key` in :file:`kitty.conf`.
For example::
map alt+s send_key ctrl+s
map ctrl+alt+2 combine : send_key ctrl+c : send_key h : send_key a
This causes the program running in kitty to receive the :kbd:`ctrl+s` key when
you press the :kbd:`alt+s` key and several keystrokes when you press
:kbd:`ctrl+alt+2`. To see this in action, run::
you press the :kbd:`alt+s` key. To see this in action, run::
kitten show-key -m kitty

View File

@@ -245,7 +245,7 @@ File paths
path must be no longer than 255 UTF-8 bytes. Total path length must be no
more than 4096 bytes. Paths from Windows systems must use the forward slash
as the separator, the first path component must be the drive letter with a
colon. For example: :file:`C:\\some\\file.txt` is represented as
colon. For example: :file:`C:\some\file.txt` is represented as
:file:`/C:/some/file.txt`. For maximum portability, the following
characters *should* be omitted from paths (however implementations are free
to try to support them returning errors for non-representable paths)::

View File

@@ -50,18 +50,6 @@ Glossary
inside kitty windows and provide it with lots of powerful and flexible
features such as viewing images, connecting conveniently to remote
computers, transferring files, inputting unicode characters, etc.
They can also be written by users in Python and used to customize and
extend kitty functionality, see :doc:`kittens_intro` for details.
easing function
A function that controls how an animation progresses over time. kitty
support the `CSS syntax for easing functions
<https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function>`__.
Commonly used easing functions are :code:`linear` for a constant rate
animation and :code:`ease-in-out` for an animation that starts slow,
becomes fast in the middle and ends slowly. These are used to control
various animations in kitty, such as :opt:`cursor_blink_interval` and
:opt:`visual_bell_duration`.
.. _env_vars:
@@ -98,11 +86,6 @@ Variables that influence kitty behavior
Same as :envvar:`VISUAL`. Used if :envvar:`VISUAL` is not set.
.. envvar:: SHELL
Specifies the default shell kitty will run when :opt:`shell` is set to
:code:`.`.
.. envvar:: GLFW_IM_MODULE
Set this to ``ibus`` to enable support for IME under X11.

View File

@@ -25,41 +25,38 @@ alpha-blending and text over graphics.
:alt: Demo of graphics rendering in kitty
:align: center
Some applications that use the kitty graphics protocol:
Some programs and libraries that use the kitty graphics protocol:
* `awrit <https://github.com/chase/awrit>`_ - Chromium-based web browser rendered in Kitty with mouse and keyboard 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
* `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
* `timg <https://github.com/hzeller/timg>`_ - a terminal image and video viewer
* `tpix <https://github.com/jesvedberg/tpix>`_ - a statically compiled binary that can be used to display images and easily installed on remote servers without root access
* `twitch-tui <https://github.com/Xithrius/twitch-tui>`_ - Twitch chat in the terminal
* `viu <https://github.com/atanunq/viu>`_ - a terminal image viewer
* `ranger <https://github.com/ranger/ranger>`_ - a terminal file manager, with image previews
* `Yazi <https://github.com/sxyazi/yazi>`_ - Blazing fast terminal file manager written in Rust, based on async I/O
Libraries:
* :doc:`kitty-diff <kittens/diff>` - a side-by-side terminal diff program with support for images
* `tpix <https://github.com/jesvedberg/tpix>`_ - a statically compiled binary that can be used to display images and easily installed on remote servers without root access
* `mpv <https://github.com/mpv-player/mpv/commit/874e28f4a41a916bb567a882063dd2589e9234e1>`_ - A video player that can play videos in the terminal
* `pixcat <https://github.com/mirukana/pixcat>`_ - a third party CLI and python library that wraps the graphics protocol
* `neofetch <https://github.com/dylanaraps/neofetch>`_ - A command line system
information tool
* `viu <https://github.com/atanunq/viu>`_ - a terminal image viewer
* `ctx.graphics <https://ctx.graphics/>`_ - Library for drawing graphics
* `timg <https://github.com/hzeller/timg>`_ - a terminal image and video viewer
* `notcurses <https://github.com/dankamongmen/notcurses>`_ - C library for terminal graphics with bindings for C++, Rust and Python
* `rasterm <https://github.com/BourgeoisBear/rasterm>`_ - Go library to display images in the terminal
* `chafa <https://github.com/hpjansson/chafa>`_ - a terminal image viewer
* `hologram.nvim <https://github.com/edluffy/hologram.nvim>`_ - view images inside nvim
* `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
* `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
* `twitch-tui <https://github.com/Xithrius/twitch-tui>`_ - Twitch chat in the terminal
* `awrit <https://github.com/chase/awrit>`_ - Chromium-based web browser rendered in Kitty with mouse and keyboard support
* `fzf <https://github.com/junegunn/fzf/commit/d8188fce7b7bea982e7f9050c35e488e49fb8fd0>`_ - A command line fuzzy finder
Other terminals that have implemented the graphics protocol:
* `WezTerm <https://github.com/wez/wezterm/issues/986>`_
* `Konsole <https://invent.kde.org/utilities/konsole/-/merge_requests/594>`_
* `wayst <https://github.com/91861/wayst>`_
* `WezTerm <https://github.com/wez/wezterm/issues/986>`_
Getting the window size
@@ -233,7 +230,7 @@ This is a so-called *Application Programming Command (APC)*. Most terminal
emulators ignore APC codes, making it safe to use.
The control data is a comma-separated list of ``key=value`` pairs. The payload
is arbitrary binary data, :rfc:`base64 <4648>` encoded to prevent interoperation problems
is arbitrary binary data, base64-encoded to prevent interoperation problems
with legacy terminals that get confused by control codes within an APC code.
The meaning of the payload is interpreted based on the control data.
@@ -294,8 +291,7 @@ compression is supported, which is specified using ``o=z``. For example::
<ESC>_Gf=24,s=10,v=20,o=z;<payload><ESC>\
This is the same as the example from the RGB data section, except that the
payload is now compressed using deflate (this occurs prior to
:rfc:`base64 <4648>` encoding).
payload is now compressed using deflate (this occurs prior to base64-encoding).
The terminal emulator will decompress it before rendering. You can specify
compression for any format. The terminal emulator will decompress before
interpreting the pixel data.
@@ -367,7 +363,7 @@ Remote clients, those that are unable to use the filesystem/shared memory to
transmit data, must send the pixel data directly using escape codes. Since
escape codes are of limited maximum length, the data will need to be chunked up
for transfer. This is done using the ``m`` key. The pixel data must first be
:rfc:`base64 <4648>` encoded then chunked up into chunks no larger than ``4096`` bytes. All
base64 encoded then chunked up into chunks no larger than ``4096`` bytes. All
chunks, except the last, must have a size that is a multiple of 4. The client
then sends the graphics escape code as usual, with the addition of an ``m`` key
that must have the value ``1`` for all but the last chunk, where it must be
@@ -754,9 +750,6 @@ deleted, if the capital letter form above is specified. Also, when the terminal
is running out of quota space for new images, existing images without
placements will be preferentially deleted.
If an image is being loaded in chunks and the upload is not complete when any
delete command is received, the partial upload must be aborted.
Some examples::
<ESC>_Ga=d<ESC>\ # delete all visible placements
@@ -960,7 +953,7 @@ by the ``C`` key with the default being to alpha blend the source rectangle
onto the destination rectangle. With ``C=1`` it will be a simple replacement
of pixels. For example::
<ESC>_Ga=c,i=1,r=7,c=9,w=23,h=27,X=4,Y=8,x=1,y=3<ESC>\
<ESC>_Gi=1,r=7,c=9,w=23,h=27,X=4,Y=8,x=1,y=3<ESC>\
Will compose a ``23x27`` rectangle located at ``(4, 8)`` in the ``7th frame``
onto the rectangle located at ``(1, 3)`` in the ``9th frame``. These will be

View File

@@ -19,14 +19,14 @@ kitty
.. tab:: Fast
* Uses GPU and SIMD vector CPU instructions for :doc:`best in class <performance>`
* Offloads rendering to the GPU for :doc:`lower system load <performance>`
* Uses threaded rendering for :iss:`absolutely minimal latency <2701#issuecomment-636497270>`
* Performance tradeoffs can be :ref:`tuned <conf-kitty-performance>`
.. tab:: Capable
* Graphics, with :doc:`images and animations <graphics-protocol>`
* Ligatures, emoji with :opt:`per glyph font substitution <symbol_map>` and :doc:`variable fonts and font features </kittens/choose-fonts>`
* Ligatures and emoji, with :opt:`per glyph font substitution <symbol_map>`
* :term:`Hyperlinks<hyperlinks>`, with :doc:`configurable actions <open_actions>`
.. tab:: Scriptable

View File

@@ -98,9 +98,6 @@ get_release_url() {
get_file_url "v$release_version" "$release_version"
}
get_version_url() {
get_file_url "v$1" "$1"
}
get_nightly_url() {
get_file_url "nightly" "nightly"
@@ -111,7 +108,6 @@ get_download_url() {
case "$installer" in
"nightly") get_nightly_url ;;
"") get_release_url ;;
version-*) get_version_url "${installer#*-}";;
*) installer_is_file="y" ;;
esac
}
@@ -130,24 +126,20 @@ download_installer() {
}
}
ensure_dest() {
printf "%s\n" "Installing to $dest"
command rm -rf "$dest" || die "Failed to delete $dest"
command mkdir -p "$dest" || die "Failed to mkdir -p $dest"
command rm -rf "$dest" || die "Failed to delete $dest"
}
linux_install() {
command mkdir "$tdir/mp"
command tar -C "$tdir/mp" "-xJof" "$installer" || die "Failed to extract kitty tarball"
ensure_dest
printf "%s\n" "Installing to $dest"
command rm -rf "$dest" || die "Failed to delete $dest"
command mv "$tdir/mp" "$dest" || die "Failed to move kitty.app to $dest"
}
macos_install() {
command mkdir "$tdir/mp"
command hdiutil attach "$installer" "-mountpoint" "$tdir/mp" || die "Failed to mount kitty.dmg"
ensure_dest
printf "%s\n" "Installing to $dest"
command rm -rf "$dest"
command mkdir -p "$dest" || die "Failed to create the directory: $dest"
command ditto -v "$tdir/mp/kitty.app" "$dest"
rc="$?"
command hdiutil detach "$tdir/mp"

View File

@@ -26,12 +26,6 @@ A terminal PDF/DJVU/CBR viewer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A terminal PDF viewer
.. _tool_fancy_cat:
`fancy-cat <https://github.com/freref/fancy-cat>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A terminal PDF viewer
.. _tool_mdcat:
`mdcat <https://github.com/lunaryorn/mdcat>`_
@@ -106,20 +100,11 @@ base application that uses kitty's graphics protocol for images.
A text mode WWW browser that supports kitty's graphics protocol to display
images.
.. _tool_awrit:
`awrit <https://github.com/chase/awrit>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A full Chromium based web browser running in the terminal using kitty's
graphics protocol.
.. _tool_chawan:
`chawan <https://sr.ht/~bptato/chawan/>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A text mode WWW browser that supports kitty's graphics protocol to display
images.
.. _tool_mpv:
`mpv <https://github.com/mpv-player/mpv/commit/874e28f4a41a916bb567a882063dd2589e9234e1>`_
@@ -147,13 +132,9 @@ protocol
.. _tool_matplotlib:
matplotlib
^^^^^^^^^^^^^^
There exist multiple backends for matplotlib to draw images directly in kitty.
* `matplotlib-backend-kitty <https://github.com/jktr/matplotlib-backend-kitty>`__
* `kitcat <https://github.com/mil-ad/kitcat>`__
`matplotlib <https://github.com/jktr/matplotlib-backend-kitty>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Show matplotlib plots directly in kitty
.. _tool_KittyTerminalImage:
@@ -294,14 +275,6 @@ Keyboard based text selection for the kitty scrollback buffer.
Miscellaneous
------------------
.. tool_gattino:
`gattino <https://github.com/salvozappa/gattino>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Integrate kitty with an LLM to convert plain language prompts into shell
commands.
.. tool_kitty_smart_tab:
`kitty-smart-tab <https://github.com/yurikhan/kitty-smart-tab>`_

View File

@@ -38,17 +38,11 @@ In addition to kitty, this protocol is also implemented in:
* The `WezTerm terminal <https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html>`__
* The `alacritty terminal <https://github.com/alacritty/alacritty/pull/7125>`__
* The `rio terminal <https://github.com/raphamorim/rio/commit/cd463ca37677a0fc48daa8795ea46dadc92b1e95>`__
* The `iTerm2 terminal <https://gitlab.com/gnachman/iterm2/-/issues/10017>`__
Libraries implementing this protocol:
* The `notcurses library <https://github.com/dankamongmen/notcurses/issues/2131>`__
* The `crossterm library <https://github.com/crossterm-rs/crossterm/pull/688>`__
* The `notcurses library
<https://github.com/dankamongmen/notcurses/issues/2131>`__
* 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/>`__
Programs implementing this protocol:
* The `Vim text editor <https://github.com/vim/vim/commit/63a2e360cca2c70ab0a85d14771d3259d4b3aafa>`__
* The `Emacs text editor via the kkp package <https://github.com/benjaminor/kkp>`__
* The `Neovim text editor <https://github.com/neovim/neovim/pull/18181>`__
@@ -58,11 +52,6 @@ Programs implementing this protocol:
* 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
* The `aerc email client <https://git.sr.ht/~rjarry/aerc/commit/d73cf33c2c6c3e564ce8aff04acc329a06eafc54>`__
Shells implementing this protocol:
* The `nushell shell <https://github.com/nushell/nushell/pull/10540>`__
* The `fish shell <https://github.com/fish-shell/fish-shell/commit/8bf8b10f685d964101f491b9cc3da04117a308b4>`__

View File

@@ -45,84 +45,7 @@ will be automatically adjusted based on what you select for the regular face.
You can choose a specific style or font feature by clicking on it. A precise
value for any variable axes can be selected using the slider, in the screenshot
above, the font supports precise weight adjustment. If you are lucky the font
above the font supports precise weight adjustment. If you are lucky the font
designer has included descriptive names for font features, which will be
displayed, if not, consult the documentation of the font to see what each feature does.
.. _font_spec_syntax:
The font specification syntax
--------------------------------
If you don't like the choose fonts kitten or simply want to understand and
write font selection options into :file:`kitty.conf` yourself, read on.
There are four font face selection keys: `font_family`, `bold_font`,
`italic_font` and `bold_italic_font`. Each of these supports the syntax
described below. Their values can be of three types, either a
font family name, the keyword ``auto`` or an extended ``key=value`` syntax
for specifying font selection precisely.
If a font family name is specified kitty will use Operating System APIs to
search for a matching font. The keyword ``auto`` means kitty will choose a font
completely automatically, typically this is used for automatically selecting
bold/italic variants once the :opt:`font_family` is set. The bold and italic
variants will then automatically use the same set of features as the main face.
To specify font face selection more precisely, a ``key=value`` syntax is used.
First, let's look at a few examples::
# Select by family only, actual face selection is automatic
font_family family="Fira Code"
# Select an exact face by Postscript name
font_family postscript_name=FiraCode
# Select an exact face by family with features and variable weight
font_family family=SourceCodeVF variable_name=SourceCodeUpright features="+zero cv01=2" wght=380
The following are the known keys, any other keys are names of *variable axes*,
that is, they are used to set the variable value for some font characteristic.
``family``
A font family name. A family typically has multiple actual font faces, such
as bold and italic variants. One or more of the faces can even be variable,
allowing fine tuning of font characteristics.
``style``
A style name to choose a particular font from a given family. Useful only
with the ``family`` key, when no more precise methods for face selection
are specified. Can also be used to specify a named variable style for
variable fonts.
``postscript_name``
The actual postscript name for a font face. This allows selecting a
particular variant within a font family. But note that postscript names
are usually insufficient for selecting variable fonts.
``full_name``
This can be used to select a particular font face in a family. However, it
is less precise than ``postscript_name`` and should not generally be used.
``variable_name``
Some families with variable fonts actually contain multiple font files. For
example, a family could have variable weights with one font file containing
upright variable weight faces and another containing italic variable weight
faces. Well designed fonts use a *variable name* to distinguish between
such files. Should be used in conjunction with ``family`` to select a
particular variable font file.
``features``
A space separated list of OpenType font features to enable/disable or
select a value of, for this font. Consult the documentation for the font
family to see what features it supports and their effects. The exact syntax
for specifying features is `documented by HarfBuzz
<https://harfbuzz.github.io/harfbuzz-hb-common.html#hb-feature-from-string>`__
``system``
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.
In addition to these keys, any four letter key is treated as the name of a
variable characteristic of the font. It's value is used to set the value for
the name.

View File

@@ -19,9 +19,10 @@ Create a file in the kitty config directory, :file:`~/.config/kitty/mykitten.py`
.. code-block:: python
from typing import List
from kitty.boss import Boss
def main(args: list[str]) -> str:
def main(args: List[str]) -> str:
# this is the main entry point of the kitten, it will be executed in
# the overlay window when the kitten is launched
answer = input('Enter some text: ')
@@ -29,7 +30,7 @@ Create a file in the kitty config directory, :file:`~/.config/kitty/mykitten.py`
# handle_result() function
return answer
def handle_result(args: list[str], answer: str, target_window_id: int, boss: Boss) -> None:
def handle_result(args: List[str], answer: str, target_window_id: int, boss: Boss) -> None:
# get the kitty window into which to paste answer
w = boss.window_id_map.get(target_window_id)
if w is not None:
@@ -59,7 +60,7 @@ would pass to ``kitten @``. For example:
.. code-block:: python
def handle_result(args: list[str], answer: str, target_window_id: int, boss: Boss) -> None:
def handle_result(args: List[str], answer: str, target_window_id: int, boss: Boss) -> None:
# get the kitty window to which to send text
w = boss.window_id_map.get(target_window_id)
if w is not None:
@@ -72,9 +73,6 @@ would pass to ``kitten @``. For example:
shown above or ``--self``.
Run, ``kitten @ --help`` in a kitty terminal, to see all the remote control
commands available to you.
Passing arguments to kittens
------------------------------
@@ -100,18 +98,19 @@ like. For example:
.. code-block:: py
from typing import List
from kitty.boss import Boss
# in main, STDIN is for the kitten process and will contain
# the contents of the screen
def main(args: list[str]) -> str:
def main(args: List[str]) -> str:
return sys.stdin.read()
# in handle_result, STDIN is for the kitty process itself, rather
# than the kitten process and should not be read from.
from kittens.tui.handler import result_handler
@result_handler(type_of_input='text')
def handle_result(args: list[str], stdin_data: str, target_window_id: int, boss: Boss) -> None:
def handle_result(args: List[str], stdin_data: str, target_window_id: int, boss: Boss) -> None:
pass
@@ -169,14 +168,15 @@ Create a Python file in the :ref:`kitty config directory <confloc>`,
.. code-block:: py
from typing import List
from kitty.boss import Boss
def main(args: list[str]) -> str:
def main(args: List[str]) -> str:
pass
from kittens.tui.handler import result_handler
@result_handler(no_ui=True)
def handle_result(args: list[str], answer: str, target_window_id: int, boss: Boss) -> None:
def handle_result(args: List[str], answer: str, target_window_id: int, boss: Boss) -> None:
tab = boss.active_tab
if tab is not None:
if tab.current_layout.name == 'stack':
@@ -228,56 +228,6 @@ The function will only send the event if the program is receiving events of
that type, and will return ``True`` if it sent the event, and ``False`` if not.
.. _kitten_main_rc:
Using remote control inside the main() kitten function
------------------------------------------------------------
You can use kitty's remote control features inside the main() function of a
kitten, even without enabling remote control. This is useful if you want to
probe kitty for more information before presenting some UI to the user or if
you want the user to be able to control kitty from within your kitten's UI
rather than after it has finished running. To enable it, simply tell kitty your kitten
requires remote control, as shown in the example below::
import json
import sys
from pprint import pprint
from kittens.tui.handler import kitten_ui
@kitten_ui(allow_remote_control=True)
def main(args: list[str]) -> str:
# get the result of running kitten @ ls
cp = main.remote_control(['ls'], capture_output=True)
if cp.returncode != 0:
sys.stderr.buffer.write(cp.stderr)
raise SystemExit(cp.returncode)
output = json.loads(cp.stdout)
pprint(output)
# open a new tab with a title specified by the user
title = input('Enter the name of tab: ')
window_id = main.remote_control(['launch', '--type=tab', '--tab-title', title], check=True, capture_output=True).stdout.decode()
return window_id
:code:`allow_remote_control=True` tells kitty to run this kitten with remote
control enabled, regardless of whether it is enabled globally or not.
To run a remote control command use the :code:`main.remote_control()` function
which is a thin wrapper around Python's :code:`subprocess.run` function. Note
that by default, for security, child processes launched by your kitten cannot use remote
control, thus it is necessary to use :code:`main.remote_control()`. If you wish
to enable child processes to use remote control, call
:code:`main.allow_indiscriminate_remote_control()`.
Remote control access can be further secured by using
:code:`kitten_ui(allow_remote_control=True, remote_control_password='ls set-colors')`.
This will use a secure generated password to restrict remote control.
You can specify a space separated list of remote control commands to allow, see
:opt:`remote_control_password` for details. The password value is accessible
as :code:`main.password` and is used by :code:`main.remote_control()`
automatically.
Debugging kittens
--------------------
@@ -398,14 +348,6 @@ 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
----------------------------------------------------------
There is documentation for :doc:`developing-builtin-kittens` which are written in the Go
language.
.. _external_kittens:
Kittens created by kitty users
@@ -418,10 +360,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
`gattino <https://github.com/salvozappa/gattino>`__
Integrate kitty with an LLM to convert plain language prompts into shell commands.
:iss:`insert password <1222>`
Insert a password from a CLI password manager, taking care to only do it at
a password prompt.

View File

@@ -1,119 +0,0 @@
Developing builtin kittens
=============================
Builtin kittens in kitty are written in the Go language, with small Python
wrapper scripts to define command line options and handle UI integration.
Getting started
-----------------------
To get started with creating a builtin kitten, one that will become part of kitty
and be available as ``kitten my-kitten``, create a directory named
:file:`my_kitten` in the :file:`kittens` directory. Then, in this directory
add three, files: :file:`__init__.py` (an empty file), :file:`__main__.py` and
:file:`main.go`.
Template for `main.py`
^^^^^^^^^^^^^^^^^^^^^^
The file :file:`main.py` contains the command line option definitions for your kitten. Change the actual options and help text below as needed.
.. code-block:: python
#!/usr/bin/env python
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import sys
# See the file kitty/cli.py in the kitty sourcecode for more examples of
# the syntax for defining options
OPTIONS = r'''
--some-string-option -s
default=my_default_value
Help text for a simple option taking a string value.
--some-boolean-option -b
type=bool-set
Help text for a boolean option defaulting to false.
--some-inverted-boolean-option
type=bool-unset
Help text for a boolean option defaulting to true.
--an-integer-option
type=int
default=13
bla bla
--an-enum-option
choices=a,b,c,d
default=a
This option can only take the values a, b, c, or d
'''.format
help_text = '''\
The introductory help text for your kitten.
Can contain multiple paragraphs with :bold:`bold`
:green:`colored`, :code:`code`, :link:`links <http://url>` etc.
formatting.
Option help strings can also use this formatting.
'''
# The usage string for your kitten
usage = 'TITLE [BODY ...]'
short_description = 'some short description of your kitten it will show up when running kitten without arguments to list all kittens`
if __name__ == '__main__':
raise SystemExit('This should be run as kitten my-kitten')
elif __name__ == '__doc__':
cd = sys.cli_docs # type: ignore
cd['usage'] = usage
cd['options'] = OPTIONS
cd['help_text'] = help_text
cd['short_desc'] = short_description
Template for `main.go`
^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: go
package my_kitten
import (
"fmt"
"kitty/tools/cli"
)
var _ = fmt.Print
func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {
// Here rc is the exit code for the kitten which should be 1 or higher if err is not nil
fmt.Println("Hello world!")
fmt.Println(args)
fmt.Println(fmt.Sprintf("%#v", opts))
return
}
func EntryPoint(parent *cli.Command) {
create_cmd(parent, main)
}
Edit :file:`tools/cmd/tool/main.go`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Add the entry point of the kitten into :file:`tools/cmd/tool/main.go`.
First, import the kitten into this file. To do this, add :code:`"kitty/kittens/my_kitten"` into the :code:`import ( ... )` section at the top.
Then, add ``my_kitten.EntryPoint(root)`` into ``func KittyToolEntryPoints(root *cli.Command)`` and you are done. After running make you should
be able to test your kitten by running::
kitten my-kitten

View File

@@ -1,43 +0,0 @@
notify
==================================================
.. only:: man
Overview
--------------
Show pop-up system notifications.
.. highlight:: sh
.. versionadded:: 0.36.0
The notify kitten
The ``notify`` kitten can be used to show pop-up system notifications
from the shell. It even works over SSH. Using it is as simple as::
kitten notify "Good morning" Hello world, it is a nice day!
To add an icon, use::
kitten notify --icon-path /path/to/some/image.png "Good morning" Hello world, it is a nice day!
kitten notify --icon firefox "Good morning" Hello world, it is a nice day!
To be informed when the notification is activated::
kitten notify --wait-for-completion "Good morning" Hello world, it is a nice day!
Then, the kitten will wait till the notification is either closed or activated.
If activated, a ``0`` is printed to :file:`STDOUT`. You can press the
:kbd:`Esc` or :kbd:`Ctrl+c` keys to abort, closing the notification.
To add buttons to the notification::
kitten notify --wait-for-completion --button One --button Two "Good morning" Hello world, it is a nice day!
.. program:: kitty +kitten notify
.. tip:: Learn about the underlying :doc:`/desktop-notifications` escape code protocol.
.. include:: /generated/cli-kitten-notify.rst

View File

@@ -116,14 +116,14 @@ this could be achieved using the ssh kitten with :program:`zsh` and
hostname myserver-*
# Setup zsh to read its files from my-conf/zsh
env ZDOTDIR=$HOME/my-conf/zsh
env ZDOTDIR $HOME/my-conf/zsh
copy --dest my-conf/zsh/.zshrc .zshrc
copy --dest my-conf/zsh/.zshenv .zshenv
# If you use other zsh init files add them in a similar manner
# Setup vim to read its config from my-conf/vim
env VIMINIT=$HOME/my-conf/vim/vimrc
env VIMRUNTIME=$HOME/my-conf/vim
env VIMINIT $HOME/my-conf/vim/vimrc
env VIMRUNTIME $HOME/my-conf/vim
copy --dest my-conf/vim .vim
copy --dest my-conf/vim/vimrc .vimrc

View File

@@ -42,42 +42,6 @@ existing color settings in :file:`kitty.conf` so they do not interfere.
Once that's done, the kitten sends kitty a signal to make it reload its config.
.. note::
If you want to have some color settings in your :file:`kitty.conf` that the
theme kitten does not override, move them into a separate conf file and
``include`` it into kitty.conf. The include should be placed after the
inclusion of :file:`current-theme.conf` so that the settings in it override
conflicting settings from :file:`current-theme.conf`.
.. _auto_color_scheme:
Change color themes automatically when the OS switches between light and dark
--------------------------------------------------------------------------------
.. versionadded:: 0.38.0
You can have kitty automatically change its color theme when the OS switches
between dark, light and no-preference modes. In order to do this, run the theme
kitten as normal and at the final screen select the option to save your chosen
theme as either light, dark, or no-preference. Repeat until you have chosen
a theme for each of the three modes. Then, once you restart kitty, it will
automatically use your chosen themes depending on the OS color scheme.
This works by creating three files: :file:`dark-theme.auto.conf`,
:file:`light-theme.auto.conf` and :file:`no-preference-theme.auto.conf` in the
kitty config directory. When these files exist, kitty queries the OS for its color scheme
and uses the appropriate file. Note that the colors in these files override all other
colors, even those specified using the :option:`kitty --override` command line flag.
kitty will also automatically change colors when the OS color scheme changes,
for example, during night/day transitions.
When using these colors, you can still dynamically change colors, but the next
time the OS changes its color mode, any dynamics changes will be overridden.
Using your own themes
-----------------------

View File

@@ -114,76 +114,49 @@ Watching launched windows
The :option:`launch --watcher` option allows you to specify Python functions
that will be called at specific events, such as when the window is resized or
closed. Note that you can also specify watchers that are loaded for all windows,
via :opt:`watcher`. To create a watcher, specify the path to a Python module
that specifies callback functions for the events you are interested in, for
create :file:`~/.config/kitty/mywatcher.py` and use :option:`launch --watcher` = :file:`mywatcher.py`:
closed. Simply specify the path to a Python module that specifies callback
functions for the events you are interested in, for example:
.. code-block:: python
# ~/.config/kitty/mywatcher.py
from typing import Any
from typing import Any, Dict
from kitty.boss import Boss
from kitty.window import Window
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 initializztion/one
# time setup. Any exceptions in this function are printed to kitty's
# STDERR but otherwise ignored.
...
def on_resize(boss: Boss, window: Window, data: dict[str, Any]) -> None:
def on_resize(boss: Boss, window: Window, data: Dict[str, Any]) -> None:
# Here data will contain old_geometry and new_geometry
# Note that resize is also called the first time a window is created
# which can be detected as old_geometry will have all zero values, in
# particular, old_geometry.xnum and old_geometry.ynum will be zero.
...
def on_focus_change(boss: Boss, window: Window, data: dict[str, Any])-> None:
def on_focus_change(boss: Boss, window: Window, data: Dict[str, Any])-> None:
# Here data will contain focused
...
def on_close(boss: Boss, window: Window, data: dict[str, Any])-> None:
def on_close(boss: Boss, window: Window, data: Dict[str, Any])-> None:
# called when window is closed, typically when the program running in
# it exits
...
def on_set_user_var(boss: Boss, window: Window, data: dict[str, Any]) -> None:
def on_set_user_var(boss: Boss, window: Window, data: Dict[str, Any]) -> None:
# called when a "user variable" is set or deleted on a window. Here
# data will contain key and value
...
def on_title_change(boss: Boss, window: Window, data: dict[str, Any]) -> None:
def on_title_change(boss: Boss, window: Window, data: Dict[str, Any]) -> None:
# called when the window title is changed on a window. Here
# data will contain title and from_child. from_child will be True
# when a title change was requested via escape code from the program
# running in the terminal
...
def on_cmd_startstop(boss: Boss, window: Window, data: dict[str, Any]) -> None:
def on_cmd_startstop(boss: Boss, window: Window, data: Dict[str, Any]) -> None:
# called when the shell starts/stops executing a command. Here
# data will contain is_start, cmdline and time.
...
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
the watcher scripts, however kitty internals are not documented/stable so for
most things you are better off using the kitty :doc:`Remote control API </remote-control>`.
Simply call :code:`boss.call_remote_control()`, with the same arguments you
would pass to ``kitten @``. For example:
.. code-block:: python
def on_resize(boss: Boss, window: Window, data: dict[str, Any]) -> None:
# send some text to the resized window
boss.call_remote_control(window, ('send-text', f'--match=id:{window.id}', 'hello world'))
Run, ``kitten @ --help`` in a kitty terminal, to see all the remote control
commands available to you.
that contains event dependent data. Some useful methods and attributes for the
``Window`` object are: ``as_text(as_ans=False, add_history=False,
add_wrap_markers=False, alternate_screen=False)`` with which you can get the
contents of the window and its scrollback buffer. Similarly,
``window.child.pid`` is the PID of the processes that was launched
in the window and ``window.id`` is the internal kitty ``id`` of the window.
Finding executables

View File

@@ -184,13 +184,11 @@ in a split using the ``rotate`` action with an argument of ``180`` and rotate
and swap with an argument of ``270``.
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
be placed side by side and a value of ``vertical`` (same as
``--location=hsplit``) means the two windows will be placed one on top of the
other. A value of ``auto`` means the axis of the split is chosen automatically
(same as ``--location=split``). By default::
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 be
placed side by side and a value of ``vertical`` (same as ``--location=hsplit``)
means the two windows will be placed one on top of the other. By default::
enabled_layouts splits:split_axis=horizontal

View File

@@ -201,8 +201,8 @@ In order to make this work, you need to configure your editor as show below:
In :file:`~/.vimrc` add:
.. code-block:: vim
let &t_ti = &t_ti . "\033]1337;SetUserVar=in_editor=MQo\007"
let &t_te = &t_te . "\033]1337;SetUserVar=in_editor\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

View File

@@ -1,38 +0,0 @@
Miscellaneous protocol extensions
==============================================
These are a few small protocol extensions kitty implements, primarily for use
by its own kitten, they are documented here for completeness.
Simple save/restore of all terminal modes
--------------------------------------------
XTerm has the XTSAVE/XTRESTORE escape codes to save and restore terminal
private modes. However, they require specifying an explicit list of modes to
save/restore. kitty extends this protocol to specify that when no modes are
specified, all side-effect free modes should be saved/restored. By side-effects
we mean things that can affect other terminal state such as cursor position or
screen contents. Examples of modes that have side effects are: `DECOM
<https://vt100.net/docs/vt510-rm/DECOM.html>`__ and `DECCOLM
<https://vt100.net/docs/vt510-rm/DECCOLM.html>`__.
This allows TUI applications to easily save and restore emulator state without
needing to maintain lists of modes.
Independent control of bold and faint SGR properties
-------------------------------------------------------
In common terminal usage, bold is set via SGR 1 and faint by SGR 2. However,
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.
kitty specific private escape codes
---------------------------------------
These are a family of escape codes used by kitty for various things including
remote control. They are all DCS (Device Control String) escape codes starting
with ``\x1b P @ kitty-`` (ignoring spaces present for clarity).

View File

@@ -1,61 +0,0 @@
#!/usr/bin/env python
# A sample script to process notifications. Save it as
# ~/.config/kitty/notifications.py
import subprocess
from kitty.notifications import NotificationCommand, Urgency
def log_notification(nc: NotificationCommand) -> None:
# Log notifications to /tmp/notifications-log.txt
with open('/tmp/notifications-log.txt', 'a') as log:
print(f'title: {nc.title}', file=log)
print(f'body: {nc.body}', file=log)
print(f'app: {nc.application_name}', file=log)
print(f'types: {nc.notification_types}', file=log)
print('\n', file=log)
def on_notification_activated(nc: NotificationCommand, which: int) -> None:
# do something when this notification is activated (clicked on)
# remember to assign this to the on_activation field in main()
pass
def main(nc: NotificationCommand) -> bool:
'''
This function should return True to filter out the notification
'''
log_notification(nc)
# filter out notifications with 'unwanted' in their titles
if 'unwanted' in nc.title.lower():
return True
# force the notification to be silent
nc.sound_name = 'silent'
# filter out notifications from the application badapp
if nc.application_name == 'badapp':
return True
# filter out low urgency notifications
if nc.urgency is Urgency.Low:
return True
# replace some bad text in the notification body
nc.body = nc.body.replace('bad text', 'good text')
# run a script if this notification is from myapp and has
# type foo, passing in the title and body as command line args
# to the script.
if nc.application_name == 'myapp' and 'foo' in nc.notification_types:
subprocess.Popen(['/path/to/my/script', nc.title, nc.body])
# do some arbitrary actions when this notification is activated
nc.on_activation = on_notification_activated
# dont filter out this notification
return False

View File

@@ -146,8 +146,6 @@ option in :file:`kitty.conf`. An example, showing all available commands:
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
@@ -178,30 +176,12 @@ option in :file:`kitty.conf`. An example, showing all available commands:
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
Environment variables of the for :code:`${NAME}` or :code:`$NAME` are
expanded in the session file, except in the *arguments* (not options) to the
launch command.

View File

@@ -34,4 +34,3 @@ please do so by opening issues in the `GitHub bug tracker
color-stack
deccara
clipboard
misc-protocol

View File

@@ -456,13 +456,6 @@ to control its behavior, separated by semi-colons. They are::
k=s - this tells kitty that the secondary (PS2) prompt is starting at the
current line.
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.
kitty also optionally supports sending the cmdline going to be executed with ``<OSC>133;C`` as::
<OSC>133;C;cmdline=cmdline encoded by %q<ST>

View File

@@ -4,9 +4,10 @@
import os
import sys
from typing import List
def main(args: list[str]=sys.argv) -> None:
def main(args: List[str]=sys.argv) -> None:
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.insert(0, os.getcwd())
if len(args) == 1:

View File

@@ -5,18 +5,18 @@ import os
import subprocess
import sys
from collections import defaultdict
from typing import Any, DefaultDict, Union
from typing import Any, DefaultDict, Dict, FrozenSet, List, Tuple, Union
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__))))
KeymapType = dict[str, tuple[str, Union[frozenset[str], str]]]
KeymapType = Dict[str, Tuple[str, Union[FrozenSet[str], str]]]
def resolve_keys(keymap: KeymapType) -> DefaultDict[str, list[str]]:
ans: DefaultDict[str, list[str]] = defaultdict(list)
def resolve_keys(keymap: KeymapType) -> DefaultDict[str, List[str]]:
ans: DefaultDict[str, List[str]] = defaultdict(list)
for ch, (attr, atype) in keymap.items():
if isinstance(atype, str) and atype in ('int', 'uint'):
q = atype
@@ -45,7 +45,7 @@ def parse_key(keymap: KeymapType) -> str:
return ' \n'.join(lines)
def parse_flag(keymap: KeymapType, type_map: dict[str, Any], command_class: str) -> str:
def parse_flag(keymap: KeymapType, type_map: Dict[str, Any], command_class: str) -> str:
lines = []
for ch in type_map['flag']:
attr, allowed_values = keymap[ch]
@@ -63,14 +63,14 @@ def parse_flag(keymap: KeymapType, type_map: dict[str, Any], command_class: str)
return ' \n'.join(lines)
def parse_number(keymap: KeymapType) -> tuple[str, str]:
def parse_number(keymap: KeymapType) -> Tuple[str, str]:
int_keys = [f'I({attr})' for attr, atype in keymap.values() if atype == 'int']
uint_keys = [f'U({attr})' for attr, atype in keymap.values() if atype == 'uint']
return '; '.join(int_keys), '; '.join(uint_keys)
def cmd_for_report(report_name: str, keymap: KeymapType, type_map: dict[str, Any], payload_allowed: bool) -> str:
def group(atype: str, conv: str) -> tuple[str, str]:
def cmd_for_report(report_name: str, keymap: KeymapType, type_map: Dict[str, Any], payload_allowed: bool) -> str:
def group(atype: str, conv: str) -> Tuple[str, str]:
flag_fmt, flag_attrs = [], []
cv = {'flag': 'c', 'int': 'i', 'uint': 'I'}[atype]
for ch in type_map[atype]:
@@ -293,7 +293,7 @@ def graphics_parser() -> None:
write_header(text, 'kitty/parse-graphics-command.h')
def main(args: list[str]=sys.argv) -> None:
def main(args: List[str]=sys.argv) -> None:
graphics_parser()

View File

@@ -6,6 +6,7 @@ import os
import re
import subprocess
import sys
from typing import List
from kitty.conf.generate import write_output
@@ -15,7 +16,7 @@ if __name__ == '__main__' and not __package__:
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
def patch_color_list(path: str, colors: list[str], name: str, spc: str = ' ') -> None:
def patch_color_list(path: str, colors: List[str], name: str, spc: str = ' ') -> None:
with open(path, 'r+') as f:
raw = f.read()
colors = sorted(colors)
@@ -39,8 +40,9 @@ def patch_color_list(path: str, colors: list[str], name: str, spc: str = ' ')
subprocess.check_call(['gofmt', '-w', path])
def main(args: list[str]=sys.argv) -> None:
def main(args: List[str]=sys.argv) -> None:
from kitty.options.definition import definition
write_output('kitty', definition)
nullable_colors = []
all_colors = []
for opt in definition.iter_all_options():
@@ -50,10 +52,9 @@ def main(args: list[str]=sys.argv) -> None:
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('kitty/rc/set_colors.py', nullable_colors, 'NULLABLE')
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)
write_output('kitty', definition, f'\nnullable_colors = frozenset({{\n {nc}\n}})')
if __name__ == '__main__':

View File

@@ -4,6 +4,7 @@
import os
import subprocess
import sys
from typing import List
if __name__ == '__main__' and not __package__:
import __main__
@@ -57,7 +58,7 @@ grabbing grabbing grabbing,closedhand,dnd-none grabbing
'''
def main(args: list[str]=sys.argv) -> None:
def main(args: List[str]=sys.argv) -> None:
glfw_enum = []
css_names = []
glfw_xc_map = {}

View File

@@ -12,15 +12,20 @@ import struct
import subprocess
import sys
import tarfile
from collections.abc import Iterator, Sequence
from contextlib import contextmanager, suppress
from functools import lru_cache
from itertools import chain
from typing import (
Any,
BinaryIO,
Dict,
Iterator,
List,
Optional,
Sequence,
Set,
TextIO,
Tuple,
Union,
)
@@ -37,7 +42,6 @@ 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.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
@@ -52,7 +56,7 @@ if __name__ == '__main__' and not __package__:
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
changed: list[str] = []
changed: List[str] = []
def newer(dest: str, *sources: str) -> bool:
@@ -70,7 +74,7 @@ def newer(dest: str, *sources: str) -> bool:
# Utils {{{
def serialize_go_dict(x: Union[dict[str, int], dict[int, str], dict[int, int], dict[str, str]]) -> str:
def serialize_go_dict(x: Union[Dict[str, int], Dict[int, str], Dict[int, int], Dict[str, str]]) -> str:
ans = []
def s(x: Union[int, str]) -> str:
@@ -188,7 +192,7 @@ def kitten_cli_docs(kitten: str) -> Any:
@lru_cache
def go_options_for_kitten(kitten: str) -> tuple[Sequence[GoOption], Optional[CompletionSpec]]:
def go_options_for_kitten(kitten: str) -> Tuple[Sequence[GoOption], Optional[CompletionSpec]]:
kcd = kitten_cli_docs(kitten)
if kcd:
ospec = kcd['options']
@@ -293,7 +297,7 @@ def generate_completions_for_kitty() -> None:
# rc command wrappers {{{
json_field_types: dict[str, str] = {
json_field_types: Dict[str, str] = {
'bool': 'bool', 'str': 'escaped_string', 'list.str': '[]escaped_string', 'dict.str': 'map[escaped_string]escaped_string', 'float': 'float64', 'int': 'int',
'scroll_amount': 'any', 'spacing': 'any', 'colors': 'any',
}
@@ -315,16 +319,10 @@ def go_field_type(json_field_type: str) -> str:
class JSONField:
def __init__(self, line: str, field_to_option_map: dict[str, str], option_map: dict[str, GoOption]) -> None:
def __init__(self, line: str) -> None:
field_def = line.split(':', 1)[0]
self.required = False
self.field, self.field_type = field_def.split('/', 1)
self.go_option_name = field_to_option_map.get(self.field, self.field)
self.go_option_name = ''.join(x.capitalize() for x in self.go_option_name.split('_'))
self.omitempty = True
if fo := option_map.get(self.go_option_name):
if fo.type in ('int', 'float') and float(fo.default or 0) != 0:
self.omitempty = False
self.field_type, self.special_parser = self.field_type.partition('=')[::2]
if self.field.endswith('+'):
self.required = True
@@ -332,44 +330,45 @@ class JSONField:
self.struct_field_name = self.field[0].upper() + self.field[1:]
def go_declaration(self) -> str:
omitempty = ',omitempty' if self.omitempty else ''
return self.struct_field_name + ' ' + go_field_type(self.field_type) + f'`json:"{self.field}{omitempty}"`'
return self.struct_field_name + ' ' + go_field_type(self.field_type) + f'`json:"{self.field},omitempty"`'
def go_code_for_remote_command(name: str, cmd: RemoteCommand, template: str) -> str:
template = '\n' + template[len('//go:build exclude'):]
af: list[str] = []
af: List[str] = []
a = af.append
af.extend(cmd.args.as_go_completion_code('ans'))
od: list[str] = []
option_map: dict[str, GoOption] = {}
od: List[str] = []
option_map: Dict[str, GoOption] = {}
for o in rc_command_options(name):
option_map[o.go_var_name] = o
a(o.as_option('ans'))
if o.go_var_name in ('NoResponse', 'ResponseTimeout'):
continue
od.append(o.struct_declaration())
jd: list[str] = []
jd: List[str] = []
json_fields = []
field_types: dict[str, str] = {}
field_types: Dict[str, str] = {}
for line in cmd.protocol_spec.splitlines():
line = line.strip()
if ':' not in line:
continue
f = JSONField(line, cmd.field_to_option_map or {}, option_map)
f = JSONField(line)
json_fields.append(f)
field_types[f.field] = f.field_type
jd.append(f.go_declaration())
jc: list[str] = []
handled_fields: set[str] = set()
jc: List[str] = []
handled_fields: Set[str] = set()
jc.extend(cmd.args.as_go_code(name, field_types, handled_fields))
unhandled = {}
used_options = set()
for field in json_fields:
if field.go_option_name in option_map:
o = option_map[field.go_option_name]
used_options.add(field.go_option_name)
oq = (cmd.field_to_option_map or {}).get(field.field, field.field)
oq = ''.join(x.capitalize() for x in oq.split('_'))
if oq in option_map:
o = option_map[oq]
used_options.add(oq)
optstring = f'options_{name}.{o.go_var_name}'
if field.special_parser:
optstring = f'{field.special_parser}({optstring})'
@@ -427,7 +426,7 @@ def go_code_for_remote_command(name: str, cmd: RemoteCommand, template: str) ->
# kittens {{{
@lru_cache
def wrapped_kittens() -> tuple[str, ...]:
def wrapped_kittens() -> Tuple[str, ...]:
with open('shell-integration/ssh/kitty') as f:
for line in f:
if line.startswith(' wrapped_kittens="'):
@@ -462,20 +461,9 @@ def generate_extra_cli_parser(name: str, spec: str) -> None:
print('}')
def kittens_needing_cli_parsers() -> Iterator[str]:
for d in os.scandir('kittens'):
if not d.is_dir(follow_symlinks=False):
continue
if os.path.exists(os.path.join(d.path, 'main.py')) and os.path.exists(os.path.join(d.path, 'main.go')):
with open(os.path.join(d.path, 'main.py')) as f:
raw = f.read()
if 'options' in raw:
yield d.name
def kitten_clis() -> None:
from kittens.runner import get_kitten_conf_docs, get_kitten_extra_cli_parsers
for kitten in kittens_needing_cli_parsers():
for kitten in wrapped_kittens() + ('pager',):
defn = get_kitten_conf_docs(kitten)
if defn is not None:
generate_conf_parser(kitten, defn)
@@ -572,7 +560,7 @@ SelectionBg: "{selbg}",
'''
def load_ref_map() -> dict[str, dict[str, str]]:
def load_ref_map() -> Dict[str, Dict[str, str]]:
with open('kitty/docs_ref_map_generated.h') as f:
raw = f.read()
raw = raw.split('{', 1)[1].split('}', 1)[0]
@@ -583,7 +571,6 @@ def load_ref_map() -> dict[str, dict[str, str]]:
def generate_constants() -> str:
from kittens.hints.main import DEFAULT_REGEX
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 FILE_TRANSFER_CODE
from kitty.options.utils import allowed_shell_integration_values, url_style_map
@@ -628,7 +615,6 @@ var RefMap = map[string]string{serialize_go_dict(ref_map['ref'])}
var DocTitleMap = map[string]string{serialize_go_dict(ref_map['doc'])}
var AllowedShellIntegrationValues = []string{{ {str(sorted(allowed_shell_integration_values))[1:-1].replace("'", '"')} }}
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 int
@@ -639,9 +625,6 @@ Select_by_word_characters: `{Options.select_by_word_characters}`, Wheel_scroll_m
Shell: "{Options.shell}", Url_excluded_characters: "{Options.url_excluded_characters}",
}}
const OptionNames = {option_names}
const DarkThemeFileName = "{ThemeFile.dark.value}"
const LightThemeFileName = "{ThemeFile.light.value}"
const NoPreferenceThemeFileName = "{ThemeFile.no_preference.value}"
''' # }}}
@@ -657,7 +640,7 @@ def replace_if_needed(path: str, show_diff: bool = False) -> Iterator[io.StringI
finally:
sys.stdout = origb
orig = ''
with suppress(FileNotFoundError), open(path) as f:
with suppress(FileNotFoundError), open(path, 'r') as f:
orig = f.read()
new = buf.getvalue()
new = f'// Code generated by {os.path.basename(__file__)}; DO NOT EDIT.\n\n' + new
@@ -673,7 +656,7 @@ def replace_if_needed(path: str, show_diff: bool = False) -> Iterator[io.StringI
@lru_cache(maxsize=256)
def rc_command_options(name: str) -> tuple[GoOption, ...]:
def rc_command_options(name: str) -> Tuple[GoOption, ...]:
cmd = command_for_name(name)
return tuple(go_options_for_seq(parse_option_spec(cmd.options_spec or '\n\n')[0]))
@@ -883,7 +866,7 @@ def start_simdgen() -> 'subprocess.Popen[bytes]':
return subprocess.Popen(['go', 'run', 'generate.go'], cwd='tools/simdstring', stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def main(args: list[str]=sys.argv) -> None:
def main(args: List[str]=sys.argv) -> None:
simdgen_process = start_simdgen()
with replace_if_needed('constants_generated.go') as f:
f.write(generate_constants())

View File

@@ -6,7 +6,7 @@ import string
import subprocess
import sys
from pprint import pformat
from typing import Any, Union
from typing import Any, Dict, List, Union
if __name__ == '__main__' and not __package__:
import __main__
@@ -194,11 +194,11 @@ macos_ansi_key_codes = { # {{{
0x31: ord(' '),
} # }}}
functional_key_names: list[str] = []
name_to_code: dict[str, int] = {}
name_to_xkb: dict[str, str] = {}
name_to_vk: dict[str, int] = {}
name_to_macu: dict[str, str] = {}
functional_key_names: List[str] = []
name_to_code: Dict[str, int] = {}
name_to_xkb: Dict[str, str] = {}
name_to_vk: Dict[str, int] = {}
name_to_macu: Dict[str, str] = {}
start_code = 0xe000
for line in functional_key_defs.splitlines():
line = line.strip()
@@ -254,11 +254,11 @@ def patch_file(path: str, what: str, text: str, start_marker: str = '/* ', end_m
subprocess.check_call(['go', 'fmt', path])
def serialize_dict(x: dict[Any, Any]) -> str:
def serialize_dict(x: Dict[Any, Any]) -> str:
return pformat(x, indent=4).replace('{', '{\n ', 1)
def serialize_go_dict(x: Union[dict[str, int], dict[int, str], dict[int, int]]) -> str:
def serialize_go_dict(x: Union[Dict[str, int], Dict[int, str], Dict[int, int]]) -> str:
ans = []
def s(x: Union[int, str]) -> str:
@@ -332,7 +332,7 @@ def generate_functional_table() -> None:
patch_file('kitty/key_encoding.c', 'special numbers', '\n'.join(enc_lines))
code_to_name = {v: k.upper() for k, v in name_to_code.items()}
csi_map = {v: name_to_code[k] for k, v in functional_encoding_overrides.items()}
letter_trailer_codes: dict[str, int] = {
letter_trailer_codes: Dict[str, int] = {
v: functional_encoding_overrides.get(k, name_to_code.get(k, 0))
for k, v in different_trailer_functionals.items() if v in 'ABCDEHFPQRSZ'}
text = f'functional_key_number_to_name_map = {serialize_dict(code_to_name)}'
@@ -377,7 +377,7 @@ def generate_legacy_text_key_maps() -> None:
patch_file('kitty_tests/keys.py', 'legacy letter tests', '\n'.join(tests), start_marker='# ', end_marker='')
def chunks(lst: list[Any], n: int) -> Any:
def chunks(lst: List[Any], n: int) -> Any:
"""Yield successive n-sized chunks from lst."""
for i in range(0, len(lst), n):
yield lst[i:i + n]
@@ -427,7 +427,7 @@ def generate_macos_mapping() -> None:
patch_file('glfw/cocoa_window.m', 'functional to macu', '\n'.join(lines))
def main(args: list[str]=sys.argv) -> None:
def main(args: List[str]=sys.argv) -> None:
generate_glfw_header()
generate_xkb_mapping()
generate_functional_table()

View File

@@ -1,8 +1,10 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
import os
import sys
from functools import lru_cache
from typing import List
if __name__ == '__main__' and not __package__:
import __main__
@@ -18,12 +20,12 @@ def to_linear(a: float) -> float:
@lru_cache
def generate_srgb_lut(line_prefix: str = ' ') -> list[str]:
values: list[str] = []
lines: list[str] = []
def generate_srgb_lut(line_prefix: str = ' ') -> List[str]:
values: List[str] = []
lines: List[str] = []
for i in range(256):
values.append(f'{to_linear(i / 255.0):1.5f}f')
values.append('{:1.5f}f'.format(to_linear(i / 255.0)))
for i in range(16):
lines.append(line_prefix + ', '.join(values[i * 16:(i + 1) * 16]) + ',')
@@ -33,7 +35,7 @@ def generate_srgb_lut(line_prefix: str = ' ') -> list[str]:
def generate_srgb_gamma(declaration: str = 'static const GLfloat srgb_lut[256] = {', close: str = '};') -> str:
lines: list[str] = []
lines: List[str] = []
a = lines.append
a('// Generated by gen-srgb-lut.py DO NOT edit')
@@ -45,7 +47,7 @@ def generate_srgb_gamma(declaration: str = 'static const GLfloat srgb_lut[256] =
return "\n".join(lines)
def main(args: list[str]=sys.argv) -> None:
def main(args: List[str]=sys.argv) -> None:
c = generate_srgb_gamma()
with open(os.path.join('kitty', 'srgb_gamma.h'), 'w') as f:
f.write(f'{c}\n')

View File

@@ -6,7 +6,6 @@ import re
import subprocess
import sys
from collections import defaultdict
from collections.abc import Generator, Iterable
from contextlib import contextmanager
from functools import lru_cache, partial
from html.entities import html5
@@ -15,7 +14,14 @@ from operator import itemgetter
from typing import (
Callable,
DefaultDict,
Dict,
FrozenSet,
Generator,
Iterable,
List,
Optional,
Set,
Tuple,
Union,
)
from urllib.request import urlopen
@@ -52,7 +58,7 @@ def get_data(fname: str, folder: str = 'UCD') -> Iterable[str]:
@lru_cache(maxsize=2)
def unicode_version() -> tuple[int, int, int]:
def unicode_version() -> Tuple[int, int, int]:
for line in get_data("ReadMe.txt"):
m = re.search(r'Version\s+(\d+)\.(\d+)\.(\d+)', line)
if m is not None:
@@ -61,16 +67,16 @@ def unicode_version() -> tuple[int, int, int]:
# Map of class names to set of codepoints in class
class_maps: dict[str, set[int]] = {}
all_symbols: set[int] = set()
name_map: dict[int, str] = {}
word_search_map: DefaultDict[str, set[int]] = defaultdict(set)
class_maps: Dict[str, Set[int]] = {}
all_symbols: Set[int] = set()
name_map: Dict[int, str] = {}
word_search_map: DefaultDict[str, Set[int]] = defaultdict(set)
soft_hyphen = 0xad
flag_codepoints = frozenset(range(0x1F1E6, 0x1F1E6 + 26))
# See https://github.com/harfbuzz/harfbuzz/issues/169
marks = set(emoji_skin_tone_modifiers) | flag_codepoints
not_assigned = set(range(0, sys.maxunicode))
property_maps: dict[str, set[int]] = defaultdict(set)
property_maps: Dict[str, Set[int]] = defaultdict(set)
def parse_prop_list() -> None:
@@ -112,7 +118,7 @@ def parse_ucd() -> None:
category = parts[2]
s = class_maps.setdefault(category, set())
desc = parts[1]
codepoints: Union[tuple[int, ...], Iterable[int]] = (codepoint,)
codepoints: Union[Tuple[int, ...], Iterable[int]] = (codepoint,)
if first is None:
if desc.endswith(', First>'):
first = codepoint
@@ -153,7 +159,7 @@ def parse_ucd() -> None:
word_search_map['diamond'] |= word_search_map['gem']
def parse_range_spec(spec: str) -> set[int]:
def parse_range_spec(spec: str) -> Set[int]:
spec = spec.strip()
if '..' in spec:
chars_ = tuple(map(lambda x: int(x, 16), filter(None, spec.split('.'))))
@@ -163,17 +169,17 @@ def parse_range_spec(spec: str) -> set[int]:
return chars
def split_two(line: str) -> tuple[set[int], str]:
def split_two(line: str) -> Tuple[Set[int], str]:
spec, rest = line.split(';', 1)
spec, rest = spec.strip(), rest.strip().split(' ', 1)[0].strip()
return parse_range_spec(spec), rest
all_emoji: set[int] = set()
emoji_presentation_bases: set[int] = set()
narrow_emoji: set[int] = set()
wide_emoji: set[int] = set()
flags: dict[int, list[int]] = {}
all_emoji: Set[int] = set()
emoji_presentation_bases: Set[int] = set()
narrow_emoji: Set[int] = set()
wide_emoji: Set[int] = set()
flags: Dict[int, List[int]] = {}
def parse_basic_emoji(spec: str) -> None:
@@ -237,13 +243,13 @@ def parse_emoji() -> None:
parse_emoji_modifier_sequence(data)
doublewidth: set[int] = set()
ambiguous: set[int] = set()
doublewidth: Set[int] = set()
ambiguous: Set[int] = set()
def parse_eaw() -> None:
global doublewidth, ambiguous
seen: set[int] = set()
seen: Set[int] = set()
for line in get_data('ucd/EastAsianWidth.txt'):
chars, eaw = split_two(line)
if eaw == 'A':
@@ -259,7 +265,7 @@ def parse_eaw() -> None:
doublewidth |= set(range(0x30000, 0x3FFFD + 1)) - seen
def get_ranges(items: list[int]) -> Generator[Union[int, tuple[int, int]], None, None]:
def get_ranges(items: List[int]) -> Generator[Union[int, Tuple[int, int]], None, None]:
items.sort()
for k, g in groupby(enumerate(items), lambda m: m[0]-m[1]):
group = tuple(map(itemgetter(1), g))
@@ -270,7 +276,7 @@ def get_ranges(items: list[int]) -> Generator[Union[int, tuple[int, int]], None,
yield a, b
def write_case(spec: Union[tuple[int, ...], int], p: Callable[..., None], for_go: bool = False) -> None:
def write_case(spec: Union[Tuple[int, ...], int], p: Callable[..., None], for_go: bool = False) -> None:
if isinstance(spec, tuple):
if for_go:
v = ', '.join(f'0x{x:x}' for x in range(spec[0], spec[1] + 1))
@@ -326,13 +332,13 @@ def category_test(
classes: Iterable[str],
comment: str,
use_static: bool = False,
extra_chars: Union[frozenset[int], set[int]] = frozenset(),
exclude: Union[set[int], frozenset[int]] = frozenset(),
extra_chars: Union[FrozenSet[int], Set[int]] = frozenset(),
exclude: Union[Set[int], FrozenSet[int]] = frozenset(),
least_check_return: Optional[str] = None,
ascii_range: Optional[str] = None
) -> None:
static = 'static inline ' if use_static else ''
chars: set[int] = set()
chars: Set[int] = set()
for c in classes:
chars |= class_maps[c]
chars |= extra_chars
@@ -352,7 +358,7 @@ def category_test(
p('\treturn false;\n}\n')
def codepoint_to_mark_map(p: Callable[..., None], mark_map: list[int]) -> dict[int, int]:
def codepoint_to_mark_map(p: Callable[..., None], mark_map: List[int]) -> Dict[int, int]:
p('\tswitch(c) { // {{{')
rmap = {c: m for m, c in enumerate(mark_map)}
for spec in get_ranges(mark_map):
@@ -368,7 +374,7 @@ def codepoint_to_mark_map(p: Callable[..., None], mark_map: list[int]) -> dict[i
def classes_to_regex(classes: Iterable[str], exclude: str = '', for_go: bool = True) -> Iterable[str]:
chars: set[int] = set()
chars: Set[int] = set()
for c in classes:
chars |= class_maps[c]
for x in map(ord, exclude):
@@ -421,9 +427,31 @@ def gen_ucd() -> None:
category_test('is_word_char', p, {c for c in class_maps if c[0] in 'LN'}, 'L and N categories')
category_test('is_CZ_category', p, cz, 'C and Z categories')
category_test('is_P_category', p, {c for c in class_maps if c[0] == 'P'}, 'P category (punctuation)')
mark_map = [0] + list(sorted(marks))
p('char_type codepoint_for_mark(combining_type m) {')
p(f'\tstatic char_type map[{len(mark_map)}] =', '{', ', '.join(map(str, mark_map)), '}; // {{{ mapping }}}')
p('\tif (m < arraysz(map)) return map[m];')
p('\treturn 0;')
p('}\n')
p('combining_type mark_for_codepoint(char_type c) {')
rmap = codepoint_to_mark_map(p, mark_map)
p('}\n')
with open('kitty/unicode-data.h', 'r+') as f:
raw = f.read()
f.seek(0)
raw, num = re.subn(
r'^// START_KNOWN_MARKS.+?^// END_KNOWN_MARKS',
'// START_KNOWN_MARKS\nstatic const combining_type '
f'VS15 = {rmap[0xfe0e]}, VS16 = {rmap[0xfe0f]};'
'\n// END_KNOWN_MARKS', raw, flags=re.MULTILINE | re.DOTALL)
if not num:
raise SystemExit('Faile dto patch mark definitions in unicode-data.h')
f.truncate()
f.write(raw)
def gen_names() -> None:
aliases_map: dict[int, set[str]] = {}
aliases_map: Dict[int, Set[str]] = {}
for word, codepoints in word_search_map.items():
for cp in codepoints:
aliases_map.setdefault(cp, set()).add(word)
@@ -442,10 +470,10 @@ def gen_names() -> None:
def gen_wcwidth() -> None:
seen: set[int] = set()
seen: Set[int] = set()
non_printing = class_maps['Cc'] | class_maps['Cf'] | class_maps['Cs']
def add(p: Callable[..., None], comment: str, chars_: Union[set[int], frozenset[int]], ret: int, for_go: bool = False) -> None:
def add(p: Callable[..., None], comment: str, chars_: Union[Set[int], FrozenSet[int]], ret: int, for_go: bool = False) -> None:
chars = chars_ - seen
seen.update(chars)
p(f'\t\t// {comment} ({len(chars)} codepoints)' + ' {{' '{')
@@ -554,7 +582,7 @@ def gen_rowcolumn_diacritics() -> None:
subprocess.check_call(['gofmt', '-w', '-s', go_file])
def main(args: list[str]=sys.argv) -> None:
def main(args: List[str]=sys.argv) -> None:
parse_ucd()
parse_prop_list()
parse_emoji()

View File

@@ -1,160 +0,0 @@
/*
* cocoa_displaylink.m
* Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
// CVDisplayLink is deprecated replace with CADisplayLink via [NSScreen displayLink] once base macOS version is 14
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include "internal.h"
#include <CoreVideo/CVDisplayLink.h>
#define DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL s_to_monotonic_t(30ll)
typedef struct _GLFWDisplayLinkNS
{
CVDisplayLinkRef displayLink;
CGDirectDisplayID displayID;
monotonic_t lastRenderFrameRequestedAt, first_unserviced_render_frame_request_at;
} _GLFWDisplayLinkNS;
static struct {
_GLFWDisplayLinkNS entries[256];
size_t count;
} displayLinks = {0};
static CGDirectDisplayID
displayIDForWindow(_GLFWwindow *w) {
NSWindow *nw = w->ns.object;
NSDictionary *dict = [nw.screen deviceDescription];
NSNumber *displayIDns = dict[@"NSScreenNumber"];
if (displayIDns) return [displayIDns unsignedIntValue];
return (CGDirectDisplayID)-1;
}
void
_glfwClearDisplayLinks(void) {
for (size_t i = 0; i < displayLinks.count; i++) {
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 CVReturn
displayLinkCallback(
CVDisplayLinkRef displayLink UNUSED,
const CVTimeStamp* now UNUSED, const CVTimeStamp* outputTime UNUSED,
CVOptionFlags flagsIn UNUSED, CVOptionFlags* flagsOut UNUSED, void* userInfo) {
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);
CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, (void*)(uintptr_t)entry->displayID);
}
unsigned
_glfwCreateDisplayLink(CGDirectDisplayID displayID) {
if (displayLinks.count >= arraysz(displayLinks.entries) - 1) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Too many monitors cannot create display link");
return displayLinks.count;
}
for (unsigned i = 0; i < displayLinks.count; i++) {
// already created in this run
if (displayLinks.entries[i].displayID == displayID) return i;
}
_GLFWDisplayLinkNS *entry = &displayLinks.entries[displayLinks.count++];
memset(entry, 0, sizeof(_GLFWDisplayLinkNS));
entry->displayID = displayID;
_glfw_create_cv_display_link(entry);
return displayLinks.count - 1;
}
static unsigned long long display_link_shutdown_timer = 0;
static void
_glfwShutdownCVDisplayLink(unsigned long long timer_id UNUSED, void *user_data UNUSED) {
display_link_shutdown_timer = 0;
for (size_t i = 0; i < displayLinks.count; i++) {
_GLFWDisplayLinkNS *dl = &displayLinks.entries[i];
if (dl->displayLink) CVDisplayLinkStop(dl->displayLink);
dl->lastRenderFrameRequestedAt = 0;
dl->first_unserviced_render_frame_request_at = 0;
}
}
void
_glfwRequestRenderFrame(_GLFWwindow *w) {
CGDirectDisplayID displayID = displayIDForWindow(w);
if (display_link_shutdown_timer) {
_glfwPlatformUpdateTimer(display_link_shutdown_timer, DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, true);
} else {
display_link_shutdown_timer = _glfwPlatformAddTimer(DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, false, _glfwShutdownCVDisplayLink, NULL, NULL);
}
monotonic_t now = glfwGetTime();
bool found_display_link = false;
_GLFWDisplayLinkNS *dl = NULL;
for (size_t i = 0; i < displayLinks.count; i++) {
dl = &displayLinks.entries[i];
if (dl->displayID == displayID) {
found_display_link = true;
dl->lastRenderFrameRequestedAt = now;
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;
_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);
}
} 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;
}
}
if (!found_display_link) {
unsigned idx = _glfwCreateDisplayLink(displayID);
if (idx < displayLinks.count) {
dl = &displayLinks.entries[idx];
dl->lastRenderFrameRequestedAt = now;
dl->first_unserviced_render_frame_request_at = now;
if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink);
}
}
}
void
_glfwDispatchRenderFrame(CGDirectDisplayID displayID) {
_GLFWwindow *w = _glfw.windowListHead;
while (w) {
if (w->ns.renderFrameRequested && displayID == displayIDForWindow(w)) {
w->ns.renderFrameRequested = false;
w->ns.renderFrameCallback((GLFWwindow*)w);
}
w = w->next;
}
for (size_t i = 0; i < displayLinks.count; i++) {
_GLFWDisplayLinkNS *dl = &displayLinks.entries[i];
if (dl->displayID == displayID) {
dl->first_unserviced_render_frame_request_at = 0;
}
}
}

View File

@@ -579,7 +579,7 @@ is_shiftable_shortcut(int scv) {
return scv == kSHKMoveFocusToActiveOrNextWindow || scv == kSHKMoveFocusToNextWindow;
}
#define USEFUL_MODS(x) (x & (NSEventModifierFlagShift | NSEventModifierFlagOption | NSEventModifierFlagCommand | NSEventModifierFlagControl | NSEventModifierFlagFunction))
#define USEFUL_MODS(x) (x & (NSEventModifierFlagShift | NSEventModifierFlagOption | NSEventModifierFlagCommand | NSEventModifierFlagControl))
static void
build_global_shortcuts_lookup(void) {

View File

@@ -35,6 +35,7 @@
#include <IOKit/graphics/IOGraphicsLib.h>
#include <CoreVideo/CVBase.h>
#include <CoreVideo/CVDisplayLink.h>
#include <ApplicationServices/ApplicationServices.h>
@@ -322,7 +323,54 @@ static double getFallbackRefreshRate(CGDirectDisplayID displayID)
////// GLFW internal API //////
//////////////////////////////////////////////////////////////////////////
void _glfwClearDisplayLinks(void) {
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
if (_glfw.ns.displayLinks.entries[i].displayLink) {
CVDisplayLinkStop(_glfw.ns.displayLinks.entries[i].displayLink);
CVDisplayLinkRelease(_glfw.ns.displayLinks.entries[i].displayLink);
}
}
memset(_glfw.ns.displayLinks.entries, 0, sizeof(_GLFWDisplayLinkNS) * _glfw.ns.displayLinks.count);
_glfw.ns.displayLinks.count = 0;
}
static CVReturn displayLinkCallback(
CVDisplayLinkRef displayLink UNUSED,
const CVTimeStamp* now UNUSED, const CVTimeStamp* outputTime UNUSED,
CVOptionFlags flagsIn UNUSED, CVOptionFlags* flagsOut UNUSED, void* userInfo)
{
CGDirectDisplayID displayID = (uintptr_t)userInfo;
NSNumber *arg = [NSNumber numberWithUnsignedInt:displayID];
[NSApp performSelectorOnMainThread:@selector(render_frame_received:) withObject:arg waitUntilDone:NO];
[arg release];
return kCVReturnSuccess;
}
void
_glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry) {
CVDisplayLinkCreateWithCGDisplay(entry->displayID, &entry->displayLink);
CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, (void*)(uintptr_t)entry->displayID);
}
_GLFWDisplayLinkNS*
_glfw_create_display_link(CGDirectDisplayID displayID) {
if (_glfw.ns.displayLinks.count >= arraysz(_glfw.ns.displayLinks.entries) - 1) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Too many monitors cannot create display link");
return NULL;
}
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
// already created in this run
if (_glfw.ns.displayLinks.entries[i].displayID == displayID) return _glfw.ns.displayLinks.entries + i;
}
_GLFWDisplayLinkNS *entry = &_glfw.ns.displayLinks.entries[_glfw.ns.displayLinks.count++];
memset(entry, 0, sizeof(_GLFWDisplayLinkNS));
entry->displayID = displayID;
_glfw_create_cv_display_link(entry);
return entry;
}
// Poll for changes in the set of connected monitors
//
void _glfwPollMonitorsNS(void)
{
uint32_t displayCount;
@@ -378,7 +426,7 @@ void _glfwPollMonitorsNS(void)
{
disconnected[j]->ns.displayID = displays[i];
disconnected[j]->ns.screen = screen;
_glfwCreateDisplayLink(displays[i]);
_glfw_create_display_link(displays[i]);
disconnected[j] = NULL;
break;
}
@@ -399,7 +447,7 @@ void _glfwPollMonitorsNS(void)
monitor->ns.displayID = displays[i];
monitor->ns.unitNumber = unitNumber;
monitor->ns.screen = screen;
_glfwCreateDisplayLink(monitor->ns.displayID);
_glfw_create_display_link(monitor->ns.displayID);
free(name);

25
glfw/cocoa_platform.h vendored
View File

@@ -30,8 +30,10 @@
#include <Carbon/Carbon.h>
#if defined(__OBJC__)
#import <Cocoa/Cocoa.h>
#import <CoreVideo/CoreVideo.h>
#else
typedef void* id;
typedef void* CVDisplayLinkRef;
#endif
// NOTE: Many Cocoa enum values have been renamed and we need to build across
@@ -158,6 +160,13 @@ typedef struct _GLFWwindowNS
GLFWcocoarenderframefun resizeCallback;
} _GLFWwindowNS;
typedef struct _GLFWDisplayLinkNS
{
CVDisplayLinkRef displayLink;
CGDirectDisplayID displayID;
monotonic_t lastRenderFrameRequestedAt, first_unserviced_render_frame_request_at;
} _GLFWDisplayLinkNS;
// Cocoa-specific global data
//
typedef struct _GLFWlibraryNS
@@ -190,6 +199,10 @@ typedef struct _GLFWlibraryNS
CFStringRef kPropertyUnicodeKeyLayoutData;
} tis;
struct {
_GLFWDisplayLinkNS entries[256];
size_t count;
} displayLinks;
// the callback to handle url open events
GLFWhandleurlopen url_open_callback;
@@ -231,16 +244,12 @@ float _glfwTransformYNS(float y);
void* _glfwLoadLocalVulkanLoaderNS(void);
// display links
void _glfwClearDisplayLinks(void);
void _glfwRestartDisplayLinks(void);
unsigned _glfwCreateDisplayLink(CGDirectDisplayID);
void _glfwDispatchRenderFrame(CGDirectDisplayID);
void _glfwRequestRenderFrame(_GLFWwindow *w);
// event loop
void _glfwDispatchTickCallback(void);
void _glfwDispatchRenderFrame(CGDirectDisplayID);
void _glfwShutdownCVDisplayLink(unsigned long long, void*);
void _glfwCocoaPostEmptyEvent(void);
void _glfw_create_cv_display_link(_GLFWDisplayLinkNS *entry);
_GLFWDisplayLinkNS* _glfw_create_display_link(CGDirectDisplayID);
uint32_t vk_to_unicode_key_with_current_layout(uint16_t keycode);

View File

@@ -306,6 +306,28 @@ static NSUInteger getStyleMask(_GLFWwindow* window)
}
CGDirectDisplayID displayIDForWindow(_GLFWwindow *w) {
NSWindow *nw = w->ns.object;
NSDictionary *dict = [nw.screen deviceDescription];
NSNumber *displayIDns = dict[@"NSScreenNumber"];
if (displayIDns) return [displayIDns unsignedIntValue];
return (CGDirectDisplayID)-1;
}
static unsigned long long display_link_shutdown_timer = 0;
#define DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL s_to_monotonic_t(30ll)
void
_glfwShutdownCVDisplayLink(unsigned long long timer_id UNUSED, void *user_data UNUSED) {
display_link_shutdown_timer = 0;
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
_GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i];
if (dl->displayLink) CVDisplayLinkStop(dl->displayLink);
dl->lastRenderFrameRequestedAt = 0;
dl->first_unserviced_render_frame_request_at = 0;
}
}
static void
requestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) {
if (!callback) {
@@ -315,7 +337,46 @@ requestRenderFrame(_GLFWwindow *w, GLFWcocoarenderframefun callback) {
}
w->ns.renderFrameCallback = callback;
w->ns.renderFrameRequested = true;
_glfwRequestRenderFrame(w);
CGDirectDisplayID displayID = displayIDForWindow(w);
if (display_link_shutdown_timer) {
_glfwPlatformUpdateTimer(display_link_shutdown_timer, DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, true);
} else {
display_link_shutdown_timer = _glfwPlatformAddTimer(DISPLAY_LINK_SHUTDOWN_CHECK_INTERVAL, false, _glfwShutdownCVDisplayLink, NULL, NULL);
}
monotonic_t now = glfwGetTime();
bool found_display_link = false;
_GLFWDisplayLinkNS *dl = NULL;
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
dl = &_glfw.ns.displayLinks.entries[i];
if (dl->displayID == displayID) {
found_display_link = true;
dl->lastRenderFrameRequestedAt = now;
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;
_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);
}
} 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;
}
}
if (!found_display_link) {
dl = _glfw_create_display_link(displayID);
if (dl) {
dl->lastRenderFrameRequestedAt = now;
dl->first_unserviced_render_frame_request_at = now;
if (!CVDisplayLinkIsRunning(dl->displayLink)) CVDisplayLinkStart(dl->displayLink);
}
}
}
void
@@ -940,7 +1001,6 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
(void)event;
if (!window) return;
_glfwInputCursorEnter(window, false);
[[NSCursor arrowCursor] set];
}
- (void)mouseEntered:(NSEvent *)event
@@ -948,16 +1008,15 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
(void)event;
if (!window) return;
_glfwInputCursorEnter(window, true);
updateCursorImage(window);
}
- (void)viewDidChangeEffectiveAppearance
{
static GLFWColorScheme appearance = GLFW_COLOR_SCHEME_NO_PREFERENCE;
GLFWColorScheme new_appearance = glfwGetCurrentSystemColorTheme(true);
GLFWColorScheme new_appearance = glfwGetCurrentSystemColorTheme();
if (new_appearance != appearance) {
appearance = new_appearance;
_glfwInputColorScheme(appearance, false);
_glfwInputColorScheme(appearance);
}
}
@@ -2278,6 +2337,24 @@ bool _glfwPlatformRawMouseMotionSupported(void)
return false;
}
void
_glfwDispatchRenderFrame(CGDirectDisplayID displayID) {
_GLFWwindow *w = _glfw.windowListHead;
while (w) {
if (w->ns.renderFrameRequested && displayID == displayIDForWindow(w)) {
w->ns.renderFrameRequested = false;
w->ns.renderFrameCallback((GLFWwindow*)w);
}
w = w->next;
}
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
_GLFWDisplayLinkNS *dl = &_glfw.ns.displayLinks.entries[i];
if (dl->displayID == displayID) {
dl->first_unserviced_render_frame_request_at = 0;
}
}
}
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
{
const NSRect contentRect = [window->ns.view frame];
@@ -3093,8 +3170,7 @@ GLFWAPI void glfwCocoaSetWindowChrome(GLFWwindow *w, unsigned int color, bool us
[window->ns.object makeFirstResponder:window->ns.view];
}}
GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialized) {
(void)query_if_unintialized;
GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(void) {
int theme_type = 0;
NSAppearance *changedAppearance = NSApp.effectiveAppearance;
NSAppearanceName newAppearance = [changedAppearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];

31
glfw/dbus_glfw.c vendored
View File

@@ -234,21 +234,12 @@ method_reply_received(DBusPendingCall *pending, void *user_data) {
}
bool
call_method_with_msg(DBusConnection *conn, DBusMessage *msg, int timeout, dbus_pending_callback callback, void *user_data, bool block) {
call_method_with_msg(DBusConnection *conn, DBusMessage *msg, int timeout, dbus_pending_callback callback, void *user_data) {
bool retval = false;
#define REPORT(errs) _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to call DBUS method: node=%s path=%s interface=%s method=%s, with error: %s", dbus_message_get_destination(msg), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), errs)
if (callback) {
DBusPendingCall *pending = NULL;
if (block) {
DBusError error; dbus_error_init(&error);
RAII_MSG(reply, dbus_connection_send_with_reply_and_block(session_bus, msg, timeout, &error));
if (dbus_error_is_set(&error)) {
callback(reply, error.message, user_data);
return false;
} else if (reply) {
callback(reply, NULL, user_data);
} else return false;
} else if (dbus_connection_send_with_reply(conn, msg, &pending, timeout)) {
if (dbus_connection_send_with_reply(conn, msg, &pending, timeout)) {
MethodResponse *res = malloc(sizeof(MethodResponse));
if (!res) return false;
res->callback = callback;
@@ -270,7 +261,7 @@ call_method_with_msg(DBusConnection *conn, DBusMessage *msg, int timeout, dbus_p
}
static bool
call_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout, dbus_pending_callback callback, void *user_data, bool blocking, va_list ap) {
call_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout, dbus_pending_callback callback, void *user_data, va_list ap) {
if (!conn || !path) return false;
RAII_MSG(msg, dbus_message_new_method_call(node, path, interface, method));
if (!msg) return false;
@@ -278,7 +269,7 @@ call_method(DBusConnection *conn, const char *node, const char *path, const char
int firstarg = va_arg(ap, int);
if ((firstarg == DBUS_TYPE_INVALID) || dbus_message_append_args_valist(msg, firstarg, ap)) {
retval = call_method_with_msg(conn, msg, timeout, callback, user_data, blocking);
retval = call_method_with_msg(conn, msg, timeout, callback, user_data);
} else {
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to call DBUS method: %s on node: %s and interface: %s could not add arguments", method, node, interface);
}
@@ -291,17 +282,7 @@ glfw_dbus_call_method_with_reply(DBusConnection *conn, const char *node, const c
bool retval;
va_list ap;
va_start(ap, user_data);
retval = call_method(conn, node, path, interface, method, timeout, callback, user_data, false, ap);
va_end(ap);
return retval;
}
bool
glfw_dbus_call_blocking_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout, dbus_pending_callback callback, void* user_data, ...) {
bool retval;
va_list ap;
va_start(ap, user_data);
retval = call_method(conn, node, path, interface, method, timeout, callback, user_data, true, ap);
retval = call_method(conn, node, path, interface, method, timeout, callback, user_data, ap);
va_end(ap);
return retval;
}
@@ -311,7 +292,7 @@ glfw_dbus_call_method_no_reply(DBusConnection *conn, const char *node, const cha
bool retval;
va_list ap;
va_start(ap, method);
retval = call_method(conn, node, path, interface, method, DBUS_TIMEOUT_USE_DEFAULT, NULL, NULL, false, ap);
retval = call_method(conn, node, path, interface, method, DBUS_TIMEOUT_USE_DEFAULT, NULL, NULL, ap);
va_end(ap);
return retval;
}

4
glfw/dbus_glfw.h vendored
View File

@@ -45,13 +45,11 @@ void glfw_dbus_terminate(_GLFWDBUSData *dbus);
DBusConnection* glfw_dbus_connect_to(const char *path, const char* err_msg, const char* name, bool register_on_bus);
void glfw_dbus_close_connection(DBusConnection *conn);
bool
call_method_with_msg(DBusConnection *conn, DBusMessage *msg, int timeout, dbus_pending_callback callback, void *user_data, bool block);
call_method_with_msg(DBusConnection *conn, DBusMessage *msg, int timeout, dbus_pending_callback callback, void *user_data);
bool
glfw_dbus_call_method_no_reply(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...);
bool
glfw_dbus_call_method_with_reply(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout_ms, dbus_pending_callback callback, void *user_data, ...);
bool
glfw_dbus_call_blocking_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout, dbus_pending_callback callback, void* user_data, ...);
void glfw_dbus_dispatch(DBusConnection *);
void glfw_dbus_session_bus_dispatch(void);
bool glfw_dbus_get_args(DBusMessage *msg, const char *failmsg, ...);

View File

@@ -323,9 +323,10 @@ def generate_wrappers(glfw_header: str) -> None:
void glfwWaylandRunWithActivationToken(GLFWwindow *handle, GLFWactivationcallback cb, void *cb_data)
bool glfwWaylandSetTitlebarColor(GLFWwindow *handle, uint32_t color, bool use_system_color)
void glfwWaylandRedrawCSDWindowTitle(GLFWwindow *handle)
void glfwWaylandSetupLayerShellForNextWindow(const GLFWLayerShellConfig *c)
void glfwWaylandSetupLayerShellForNextWindow(GLFWLayerShellConfig c)
pid_t glfwWaylandCompositorPID(void)
unsigned long long glfwDBusUserNotify(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *data)
unsigned long long glfwDBusUserNotify(const char *app_name, const char* icon, const char *summary, const char *body, \
const char *action_text, int32_t timeout, int urgency, GLFWDBusnotificationcreatedfun callback, void *data)
void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler)
int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc)
void glfwSetX11WindowAsDock(int32_t x11_window_id)
@@ -363,7 +364,7 @@ typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
typedef void (*GLFWwaylandframecallbackfunc)(unsigned long long id);
typedef void (*GLFWDBusnotificationcreatedfun)(unsigned long long, uint32_t, void*);
typedef void (*GLFWDBusnotificationactivatedfun)(uint32_t, int, const char*);
typedef void (*GLFWDBusnotificationactivatedfun)(uint32_t, const char*);
{}
const char* load_glfw(const char* path);

27
glfw/glfw3.h vendored
View File

@@ -1300,33 +1300,21 @@ typedef struct GLFWkeyevent
bool fake_event_on_focus_change;
} 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_LAYER_SHELL_NONE, GLFW_LAYER_SHELL_BACKGROUND, GLFW_LAYER_SHELL_PANEL } GLFWLayerShellType;
typedef enum { GLFW_EDGE_TOP, GLFW_EDGE_BOTTOM, GLFW_EDGE_LEFT, GLFW_EDGE_RIGHT, GLFW_EDGE_NONE } GLFWEdge;
typedef enum { GLFW_EDGE_TOP, GLFW_EDGE_BOTTOM, GLFW_EDGE_LEFT, GLFW_EDGE_RIGHT } GLFWEdge;
typedef enum { GLFW_FOCUS_NOT_ALLOWED, GLFW_FOCUS_EXCLUSIVE, GLFW_FOCUS_ON_DEMAND} GLFWFocusPolicy;
typedef struct GLFWLayerShellConfig {
GLFWLayerShellType type;
GLFWEdge edge;
char output_name[64];
const char *output_name;
GLFWFocusPolicy focus_policy;
unsigned x_size_in_cells;
unsigned y_size_in_cells;
unsigned requested_top_margin;
unsigned requested_left_margin;
unsigned requested_bottom_margin;
unsigned requested_right_margin;
int requested_exclusive_zone;
unsigned override_exclusive_zone;
unsigned size_in_cells;
void (*size_callback)(GLFWwindow *window, const struct GLFWLayerShellConfig *config, unsigned monitor_width, unsigned monitor_height, uint32_t *width, uint32_t *height);
} GLFWLayerShellConfig;
typedef struct GLFWDBUSNotificationData {
const char *app_name, *icon, *summary, *body, *category, **actions; size_t num_actions;
int32_t timeout; uint8_t urgency; uint32_t replaces; int muted;
} GLFWDBUSNotificationData;
/*! @brief The function pointer type for error callbacks.
*
* This is the function pointer type for error callbacks. An error callback
@@ -1436,17 +1424,16 @@ typedef void (* GLFWapplicationclosefun)(int);
*
* This is the function pointer type for system color theme changes.
* @code
* void function_name(GLFWColorScheme theme_type, bool is_initial_value)
* void function_name(int theme_type)
* @endcode
*
* @param[in] theme_type 0 for unknown, 1 for dark and 2 for light
* @param[in] is_initial_value true if this is the initial read of the color theme on systems where it is asynchronous such as Linux
*
* @sa @ref glfwSetSystemColorThemeChangeCallback
*
* @ingroup window
*/
typedef void (* GLFWsystemcolorthemechangefun)(GLFWColorScheme, bool);
typedef void (* GLFWsystemcolorthemechangefun)(GLFWColorScheme);
/*! @brief The function pointer type for window content refresh callbacks.
@@ -3989,7 +3976,7 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind
GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun callback);
GLFWAPI GLFWapplicationclosefun glfwSetApplicationCloseCallback(GLFWapplicationclosefun callback);
GLFWAPI GLFWsystemcolorthemechangefun glfwSetSystemColorThemeChangeCallback(GLFWsystemcolorthemechangefun callback);
GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialized);
GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(void);
/*! @brief Sets the refresh callback for the specified window.
*

7
glfw/ibus_glfw.c vendored
View File

@@ -325,12 +325,7 @@ get_ibus_address_file_name(void) {
}
offset = snprintf(ans, sizeof(ans), "%s/.config", conf_env);
}
DBusError err;
char *key = dbus_try_get_local_machine_id(&err);
if (!key) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Cannot connect to IBUS as could not get DBUS local machine id with error %s: %s", err.name ? err.name : "", err.message ? err.message : "");
return NULL;
}
char *key = dbus_get_local_machine_id();
snprintf(ans + offset, sizeof(ans) - offset, "/ibus/bus/%s-%s-%s", key, host, disp_num);
dbus_free(key);
return ans;

4
glfw/input.c vendored
View File

@@ -448,9 +448,9 @@ void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value)
js->hats[hat] = value;
}
void _glfwInputColorScheme(GLFWColorScheme value, bool is_initial_value) {
void _glfwInputColorScheme(GLFWColorScheme value) {
_glfwPlatformInputColorScheme(value);
if (_glfw.callbacks.system_color_theme_change) _glfw.callbacks.system_color_theme_change(value, is_initial_value);
if (_glfw.callbacks.system_color_theme_change) _glfw.callbacks.system_color_theme_change(value);
}
//////////////////////////////////////////////////////////////////////////

2
glfw/internal.h vendored
View File

@@ -814,7 +814,7 @@ void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods)
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
void _glfwInputCursorEnter(_GLFWwindow* window, bool entered);
int _glfwInputDrop(_GLFWwindow* window, const char *mime, const char *text, size_t sz);
void _glfwInputColorScheme(GLFWColorScheme, bool);
void _glfwInputColorScheme(GLFWColorScheme);
void _glfwPlatformInputColorScheme(GLFWColorScheme);
void _glfwInputJoystick(_GLFWjoystick* js, int event);
void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value);

View File

@@ -15,14 +15,18 @@
#define DESKTOP_INTERFACE "org.freedesktop.portal.Settings"
#define GNOME_DESKTOP_NAMESPACE "org.gnome.desktop.interface"
#define FDO_DESKTOP_NAMESPACE "org.freedesktop.appearance"
static const char* supported_namespaces[2] = {FDO_DESKTOP_NAMESPACE, GNOME_DESKTOP_NAMESPACE};
#define FDO_APPEARANCE_KEY "color-scheme"
static char theme_name[128] = {0};
static int theme_size = -1;
static GLFWColorScheme appearance = GLFW_COLOR_SCHEME_NO_PREFERENCE;
static bool cursor_theme_changed = false, appearance_initialized = false;
static bool cursor_theme_changed = false;
GLFWColorScheme
glfw_current_system_color_theme(void) {
return appearance;
}
#define HANDLER(name) static void name(DBusMessage *msg, const char* errmsg, void *data) { \
(void)data; \
@@ -31,34 +35,6 @@ static bool cursor_theme_changed = false, appearance_initialized = false;
return; \
}
HANDLER(get_color_scheme)
uint32_t val;
DBusMessageIter iter, variant_iter;
if (!dbus_message_iter_init(msg, &iter)) return;
dbus_message_iter_recurse(&iter, &variant_iter);
int type = dbus_message_iter_get_arg_type(&variant_iter);
if (type != DBUS_TYPE_UINT32) {
_glfwInputError(GLFW_PLATFORM_ERROR, "ReadOne for color-scheme did not return a uint32"); return;
}
dbus_message_iter_get_basic(&variant_iter, &val);
if (val < 3) appearance = val;
}
GLFWColorScheme
glfw_current_system_color_theme(bool query_if_unintialized) {
if (!appearance_initialized && query_if_unintialized) {
appearance_initialized = true;
DBusConnection *session_bus = glfw_dbus_session_bus();
if (session_bus) {
const char *namespace = FDO_DESKTOP_NAMESPACE, *key = FDO_APPEARANCE_KEY;
glfw_dbus_call_blocking_method(session_bus, DESKTOP_SERVICE, DESKTOP_PATH, DESKTOP_INTERFACE, "ReadOne", DBUS_TIMEOUT_USE_DEFAULT,
get_color_scheme, NULL, DBUS_TYPE_STRING, &namespace, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID);
}
}
return appearance;
}
static void
process_fdo_setting(const char *key, DBusMessageIter *value) {
if (strcmp(key, FDO_APPEARANCE_KEY) == 0) {
@@ -66,13 +42,7 @@ process_fdo_setting(const char *key, DBusMessageIter *value) {
uint32_t val;
dbus_message_iter_get_basic(value, &val);
if (val > 2) val = 0;
if (!appearance_initialized) {
appearance_initialized = true;
if (val != appearance) {
appearance = val;
_glfwInputColorScheme(appearance, true);
}
}
appearance = val;
}
}
}
@@ -141,9 +111,7 @@ HANDLER(process_desktop_settings)
if (!dbus_message_iter_next(&array)) break;
}
#undef die
#ifndef _GLFW_X11
if (cursor_theme_changed) _glfwPlatformChangeCursorTheme();
#endif
}
#undef HANDLER
@@ -155,11 +123,9 @@ read_desktop_settings(DBusConnection *session_bus) {
DBusMessageIter iter, array_iter;
dbus_message_iter_init_append(msg, &iter);
if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &array_iter)) { return false; }
for (unsigned i = 0; i < arraysz(supported_namespaces); ++i) {
if (!dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, &supported_namespaces[i])) return false;
}
if (!dbus_message_iter_close_container(&iter, &array_iter)) { return false; }
return call_method_with_msg(session_bus, msg, DBUS_TIMEOUT_USE_DEFAULT, process_desktop_settings, NULL, false);
bool ok = call_method_with_msg(session_bus, msg, DBUS_TIMEOUT_USE_DEFAULT, process_desktop_settings, NULL);
return ok;
}
void
@@ -194,8 +160,7 @@ on_color_scheme_change(DBusMessage *message) {
if (val > 2) val = 0;
if (val != appearance) {
appearance = val;
appearance_initialized = true;
_glfwInputColorScheme(appearance, false);
_glfwInputColorScheme(appearance);
}
}
break;

View File

@@ -12,4 +12,4 @@
void glfw_initialize_desktop_settings(void);
void glfw_current_cursor_theme(const char **theme, int *size);
GLFWColorScheme glfw_current_system_color_theme(bool);
GLFWColorScheme glfw_current_system_color_theme(void);

128
glfw/linux_notify.c vendored
View File

@@ -5,11 +5,9 @@
* Distributed under terms of the GPL3 license.
*/
#define _POSIX_C_SOURCE 200809L
#include "internal.h"
#include "linux_notify.h"
#include <stdlib.h>
#include <string.h>
#define NOTIFICATIONS_SERVICE "org.freedesktop.Notifications"
#define NOTIFICATIONS_PATH "/org/freedesktop/Notifications"
@@ -18,6 +16,8 @@
static inline void cleanup_free(void *p) { free(*(void**)p); }
#define RAII_ALLOC(type, name, initializer) __attribute__((cleanup(cleanup_free))) type *name = initializer
static notification_id_type notification_id = 0;
typedef struct {
notification_id_type next_id;
GLFWDBusnotificationcreatedfun callback;
@@ -41,10 +41,8 @@ notification_created(DBusMessage *msg, const char* errmsg, void *data) {
uint32_t id;
if (!glfw_dbus_get_args(msg, "Failed to get Notification uid", DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID)) return;
NotificationCreatedData *ncd = (NotificationCreatedData*)data;
if (ncd) {
if (ncd->callback) ncd->callback(ncd->next_id, id, ncd->data);
free(ncd);
}
if (ncd->callback) ncd->callback(ncd->next_id, id, ncd->data);
if (data) free(data);
}
static DBusHandlerResult
@@ -52,100 +50,35 @@ message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data U
/* printf("session_bus message_handler invoked interface: %s member: %s\n", dbus_message_get_interface(msg), dbus_message_get_member(msg)); */
if (dbus_message_is_signal(msg, NOTIFICATIONS_IFACE, "ActionInvoked")) {
uint32_t id;
const char *action = NULL;
const char *action;
if (glfw_dbus_get_args(msg, "Failed to get args from ActionInvoked notification signal",
DBUS_TYPE_UINT32, &id, DBUS_TYPE_STRING, &action, DBUS_TYPE_INVALID)) {
if (activated_handler) {
activated_handler(id, 2, action);
activated_handler(id, action);
return DBUS_HANDLER_RESULT_HANDLED;
}
}
}
if (dbus_message_is_signal(msg, NOTIFICATIONS_IFACE, "ActivationToken")) {
uint32_t id;
const char *token = NULL;
if (glfw_dbus_get_args(msg, "Failed to get args from ActivationToken notification signal",
DBUS_TYPE_UINT32, &id, DBUS_TYPE_STRING, &token, DBUS_TYPE_INVALID)) {
if (activated_handler) {
activated_handler(id, 1, token);
return DBUS_HANDLER_RESULT_HANDLED;
}
}
}
if (dbus_message_is_signal(msg, NOTIFICATIONS_IFACE, "NotificationClosed")) {
uint32_t id;
if (glfw_dbus_get_args(msg, "Failed to get args from NotificationClosed notification signal",
DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID)) {
if (activated_handler) {
activated_handler(id, 0, "");
return DBUS_HANDLER_RESULT_HANDLED;
}
}
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static bool
cancel_user_notification(DBusConnection *session_bus, uint32_t *id) {
return glfw_dbus_call_method_no_reply(session_bus, NOTIFICATIONS_SERVICE, NOTIFICATIONS_PATH, NOTIFICATIONS_IFACE, "CloseNotification", DBUS_TYPE_UINT32, id, DBUS_TYPE_INVALID);
}
static void
got_capabilities(DBusMessage *msg, const char* err, void* data UNUSED) {
if (err) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Notify: Failed to get server capabilities error: %s", err);
return;
}
#define check_call(func, err, ...) if (!func(__VA_ARGS__)) { _glfwInputError(GLFW_PLATFORM_ERROR, "Notify: GetCapabilities: %s", err); return; }
DBusMessageIter iter, array_iter;
check_call(dbus_message_iter_init, "message has no parameters", msg, &iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Notify: GetCapabilities: %s", "reply is not an array of strings");
return;
}
dbus_message_iter_recurse(&iter, &array_iter);
char buf[2048] = {0}, *p = buf, *end = buf + sizeof(buf);
while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRING) {
const char *str;
dbus_message_iter_get_basic(&array_iter, &str);
size_t len = strlen(str);
if (len && p + len + 2 < end) { p = stpcpy(p, str); *(p++) = '\n'; }
dbus_message_iter_next(&array_iter);
}
if (activated_handler) activated_handler(0, -1, buf);
#undef check_call
}
static bool
get_capabilities(DBusConnection *session_bus) {
return glfw_dbus_call_method_with_reply(session_bus, NOTIFICATIONS_SERVICE, NOTIFICATIONS_PATH, NOTIFICATIONS_IFACE, "GetCapabilities", 60, got_capabilities, NULL, DBUS_TYPE_INVALID);
}
notification_id_type
glfw_dbus_send_user_notification(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *user_data) {
glfw_dbus_send_user_notification(const char *app_name, const char* icon, const char *summary, const char *body, const char* action_name, int32_t timeout, int urgency, GLFWDBusnotificationcreatedfun callback, void *user_data) {
DBusConnection *session_bus = glfw_dbus_session_bus();
if (!session_bus) return 0;
if (n->timeout == -9999 && n->urgency == 255) return cancel_user_notification(session_bus, user_data) ? 1 : 0;
if (n->timeout == -99999 && n->urgency == 255) return get_capabilities(session_bus) ? 1 : 0;
static DBusConnection *added_signal_match = NULL;
if (!session_bus) return 0;
if (added_signal_match != session_bus) {
dbus_bus_add_match(session_bus, "type='signal',interface='" NOTIFICATIONS_IFACE "',member='ActionInvoked'", NULL);
dbus_bus_add_match(session_bus, "type='signal',interface='" NOTIFICATIONS_IFACE "',member='NotificationClosed'", NULL);
dbus_bus_add_match(session_bus, "type='signal',interface='" NOTIFICATIONS_IFACE "',member='ActivationToken'", NULL);
dbus_connection_add_filter(session_bus, message_handler, NULL, NULL);
added_signal_match = session_bus;
}
RAII_ALLOC(NotificationCreatedData, data, malloc(sizeof(NotificationCreatedData)));
if (!data) return 0;
static notification_id_type notification_id = 0;
data->next_id = ++notification_id;
data->callback = callback; data->data = user_data;
if (!data->next_id) data->next_id = ++notification_id;
uint32_t replaces_id = 0;
RAII_MSG(msg, dbus_message_new_method_call(NOTIFICATIONS_SERVICE, NOTIFICATIONS_PATH, NOTIFICATIONS_IFACE, "Notify"));
if (!msg) { return 0; }
@@ -153,38 +86,33 @@ glfw_dbus_send_user_notification(const GLFWDBUSNotificationData *n, GLFWDBusnoti
dbus_message_iter_init_append(msg, &args);
#define check_call(func, ...) if (!func(__VA_ARGS__)) { _glfwInputError(GLFW_PLATFORM_ERROR, "%s", "Out of memory allocating DBUS message for notification\n"); return 0; }
#define APPEND(to, type, val) check_call(dbus_message_iter_append_basic, &to, type, &val);
APPEND(args, DBUS_TYPE_STRING, n->app_name)
APPEND(args, DBUS_TYPE_UINT32, n->replaces)
APPEND(args, DBUS_TYPE_STRING, n->icon)
APPEND(args, DBUS_TYPE_STRING, n->summary)
APPEND(args, DBUS_TYPE_STRING, n->body)
APPEND(args, DBUS_TYPE_STRING, app_name)
APPEND(args, DBUS_TYPE_UINT32, replaces_id)
APPEND(args, DBUS_TYPE_STRING, icon)
APPEND(args, DBUS_TYPE_STRING, summary)
APPEND(args, DBUS_TYPE_STRING, body)
check_call(dbus_message_iter_open_container, &args, DBUS_TYPE_ARRAY, "s", &array);
if (n->actions) {
for (size_t i = 0; i < n->num_actions; i++) {
APPEND(array, DBUS_TYPE_STRING, n->actions[i]);
}
if (action_name) {
static const char* default_action = "default";
APPEND(array, DBUS_TYPE_STRING, default_action);
APPEND(array, DBUS_TYPE_STRING, action_name);
}
check_call(dbus_message_iter_close_container, &args, &array);
check_call(dbus_message_iter_open_container, &args, DBUS_TYPE_ARRAY, "{sv}", &array);
#define append_sv_dictionary_entry(k, val_type, val) { \
check_call(dbus_message_iter_open_container, &array, DBUS_TYPE_DICT_ENTRY, NULL, &dict); \
static const char *key = k; \
APPEND(dict, DBUS_TYPE_STRING, key); \
check_call(dbus_message_iter_open_container, &dict, DBUS_TYPE_VARIANT, val_type##_AS_STRING, &variant); \
APPEND(variant, val_type, val); \
check_call(dbus_message_iter_close_container, &dict, &variant); \
check_call(dbus_message_iter_close_container, &array, &dict); \
}
append_sv_dictionary_entry("urgency", DBUS_TYPE_BYTE, n->urgency);
if (n->category && n->category[0]) append_sv_dictionary_entry("category", DBUS_TYPE_STRING, n->category);
if (n->muted) append_sv_dictionary_entry("suppress-sound", DBUS_TYPE_BOOLEAN, n->muted);
check_call(dbus_message_iter_open_container, &array, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
static const char* urgency_key = "urgency";
APPEND(dict, DBUS_TYPE_STRING, urgency_key);
check_call(dbus_message_iter_open_container, &dict, DBUS_TYPE_VARIANT, DBUS_TYPE_BYTE_AS_STRING, &variant);
uint8_t urgencyb = urgency & 3;
APPEND(variant, DBUS_TYPE_BYTE, urgencyb);
check_call(dbus_message_iter_close_container, &dict, &variant);
check_call(dbus_message_iter_close_container, &array, &dict);
check_call(dbus_message_iter_close_container, &args, &array);
APPEND(args, DBUS_TYPE_INT32, n->timeout)
APPEND(args, DBUS_TYPE_INT32, timeout)
#undef check_call
#undef APPEND
if (!call_method_with_msg(session_bus, msg, 5000, notification_created, data, false)) return 0;
if (!call_method_with_msg(session_bus, msg, 5000, notification_created, data)) return 0;
notification_id_type ans = data->next_id;
data = NULL;
return ans;

4
glfw/linux_notify.h vendored
View File

@@ -12,8 +12,8 @@
typedef unsigned long long notification_id_type;
typedef void (*GLFWDBusnotificationcreatedfun)(notification_id_type, uint32_t, void*);
typedef void (*GLFWDBusnotificationactivatedfun)(uint32_t, int, const char*);
typedef void (*GLFWDBusnotificationactivatedfun)(uint32_t, const char*);
notification_id_type
glfw_dbus_send_user_notification(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun, void*);
glfw_dbus_send_user_notification(const char *app_name, const char* icon, const char *summary, const char *body, const char *action_name, int32_t timeout, int urgency, GLFWDBusnotificationcreatedfun, void*);
void
glfw_dbus_set_user_notification_activated_handler(GLFWDBusnotificationactivatedfun handler);

View File

@@ -13,7 +13,6 @@
"cocoa_joystick.m",
"cocoa_monitor.m",
"cocoa_window.m",
"cocoa_displaylink.m",
"posix_thread.c",
"nsgl_context.m",
"egl_context.c",
@@ -83,7 +82,6 @@
"staging/cursor-shape/cursor-shape-v1.xml",
"staging/fractional-scale/fractional-scale-v1.xml",
"staging/single-pixel-buffer/single-pixel-buffer-v1.xml",
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml",
"kwin-blur-v1.xml",
"wlr-layer-shell-unstable-v1.xml"
@@ -146,7 +144,6 @@
"linux_joystick.h",
"null_joystick.h",
"linux_notify.h",
"linux_desktop_settings.h",
"main_loop.h"
],
"sources": [
@@ -163,7 +160,6 @@
"backend_utils.c",
"linux_joystick.c",
"null_joystick.c",
"linux_desktop_settings.c",
"linux_notify.c"
]
}

View File

@@ -332,7 +332,7 @@ render_title_bar(_GLFWwindow *window, bool to_front_buffer) {
const uint32_t dark_fg = is_focused ? 0xffffffff : 0xffcccccc, dark_bg = is_focused ? 0xff303030 : 0xff242424;
static const uint32_t hover_dark_bg = 0xff444444, hover_light_bg = 0xffbbbbbb;
uint32_t bg_color = light_bg, fg_color = light_fg, hover_bg = hover_light_bg;
GLFWColorScheme appearance = glfwGetCurrentSystemColorTheme(false);
GLFWColorScheme appearance = glfwGetCurrentSystemColorTheme();
bool is_dark = false;
if (decs.use_custom_titlebar_color || appearance == GLFW_COLOR_SCHEME_NO_PREFERENCE) {
bg_color = 0xff000000 | (decs.titlebar_color & 0xffffff);
@@ -468,8 +468,10 @@ render_shadows(_GLFWwindow *window) {
static bool
create_shm_buffers(_GLFWwindow* window) {
const double scale = _glfwWaylandWindowScale(window);
decs.mapping.size = 0;
#define bp(which, width, height) decs.mapping.size += init_buffer_pair(&decs.which.buffer, width, height, decs.for_window_state.fscale);
#define bp(which, width, height) decs.mapping.size += init_buffer_pair(&decs.which.buffer, width, height, scale);
bp(titlebar, window->wl.width, decs.metrics.visible_titlebar_height);
bp(shadow_top, window->wl.width, decs.metrics.width);
bp(shadow_bottom, window->wl.width, decs.metrics.width);
@@ -502,7 +504,7 @@ create_shm_buffers(_GLFWwindow* window) {
wl_shm_pool_destroy(pool);
render_title_bar(window, true);
render_shadows(window);
debug("Created decoration buffers at scale: %f\n", decs.for_window_state.fscale);
debug("Created decoration buffers at scale: %f\n", scale);
return true;
}
@@ -575,21 +577,21 @@ ensure_csd_resources(_GLFWwindow *window) {
if (!window_is_csd_capable(window)) return false;
const bool is_focused = window->id == _glfw.focusedWindowId;
const bool focus_changed = is_focused != decs.for_window_state.focused;
const double current_scale = _glfwWaylandWindowScale(window);
const bool needs_shadow = window_needs_shadows(window);
const bool size_changed = (
decs.for_window_state.width != window->wl.width ||
decs.for_window_state.height != window->wl.height ||
decs.for_window_state.fscale != current_scale ||
decs.for_window_state.fscale != _glfwWaylandWindowScale(window) ||
!decs.mapping.data
);
const bool state_changed = decs.for_window_state.toplevel_states != window->wl.current.toplevel_states;
const bool needs_update = focus_changed || size_changed || !decs.titlebar.surface || decs.buffer_destroyed || state_changed;
debug("CSD: old.size: %dx%d new.size: %dx%d needs_update: %d size_changed: %d state_changed: %d buffer_destroyed: %d\n",
decs.for_window_state.width, decs.for_window_state.height, window->wl.width, window->wl.height, needs_update,
const bool shadow_changed = needs_shadow != decs.for_window_state.needs_shadow;
debug("CSD: old.size: %dx%d new.size: %dx%d needs_update: %d shadow_changed: %d size_changed: %d state_changed: %d buffer_destroyed: %d\n",
decs.for_window_state.width, decs.for_window_state.height, window->wl.width, window->wl.height, needs_update, shadow_changed,
size_changed, state_changed, decs.buffer_destroyed);
if (!needs_update) return false;
decs.for_window_state.fscale = current_scale; // used in create_shm_buffers
if (size_changed || decs.buffer_destroyed) {
if (size_changed || decs.buffer_destroyed || shadow_changed) {
free_csd_buffers(window);
if (!create_shm_buffers(window)) return false;
decs.buffer_destroyed = false;
@@ -600,14 +602,20 @@ ensure_csd_resources(_GLFWwindow *window) {
position_csd_surface(&decs.which, x, y);
setup_surface(titlebar, 0, -decs.metrics.visible_titlebar_height);
setup_surface(shadow_top, decs.titlebar.x, decs.titlebar.y - decs.metrics.width);
setup_surface(shadow_bottom, decs.titlebar.x, window->wl.height);
setup_surface(shadow_left, -decs.metrics.width, decs.titlebar.y);
setup_surface(shadow_right, window->wl.width, decs.shadow_left.y);
setup_surface(shadow_upper_left, decs.shadow_left.x, decs.shadow_top.y);
setup_surface(shadow_upper_right, decs.shadow_right.x, decs.shadow_top.y);
setup_surface(shadow_lower_left, decs.shadow_left.x, decs.shadow_bottom.y);
setup_surface(shadow_lower_right, decs.shadow_right.x, decs.shadow_bottom.y);
if (needs_shadow) {
setup_surface(shadow_top, decs.titlebar.x, decs.titlebar.y - decs.metrics.width);
setup_surface(shadow_bottom, decs.titlebar.x, window->wl.height);
setup_surface(shadow_left, -decs.metrics.width, decs.titlebar.y);
setup_surface(shadow_right, window->wl.width, decs.shadow_left.y);
setup_surface(shadow_upper_left, decs.shadow_left.x, decs.shadow_top.y);
setup_surface(shadow_upper_right, decs.shadow_right.x, decs.shadow_top.y);
setup_surface(shadow_lower_left, decs.shadow_left.x, decs.shadow_bottom.y);
setup_surface(shadow_lower_right, decs.shadow_right.x, decs.shadow_bottom.y);
} else {
#define d(which) free_csd_surface(&decs.which);
all_shadow_surfaces(d)
#undef d
}
if (focus_changed || state_changed) update_title_bar(window);
damage_csd(titlebar, decs.titlebar.buffer.front);
@@ -618,8 +626,10 @@ ensure_csd_resources(_GLFWwindow *window) {
decs.for_window_state.width = window->wl.width;
decs.for_window_state.height = window->wl.height;
decs.for_window_state.fscale = _glfwWaylandWindowScale(window);
decs.for_window_state.focused = is_focused;
decs.for_window_state.toplevel_states = window->wl.current.toplevel_states;
decs.for_window_state.needs_shadow = needs_shadow;
return true;
}
@@ -702,7 +712,7 @@ set_cursor(GLFWCursorShape shape, _GLFWwindow* window)
struct wl_cursor_theme *theme = glfw_wlc_theme_for_scale(scale);
if (!theme) return;
cursor = _glfwLoadCursor(shape, theme);
if (!cursor || !cursor->images) return;
if (!cursor) return;
image = cursor->images[0];
if (!image) return;
if (image->width % scale || image->height % scale) {
@@ -759,7 +769,6 @@ handle_pointer_leave(_GLFWwindow *window, struct wl_surface *surface) {
}
#undef c
decs.focus = CENTRAL_WINDOW;
decs.dragging = false;
}
@@ -769,11 +778,7 @@ handle_pointer_move(_GLFWwindow *window) {
switch (decs.focus)
{
case CENTRAL_WINDOW: break;
case CSD_titlebar: {
if (decs.dragging) {
if (window->wl.xdg.toplevel) xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.pointer_serial);
} else if (update_hovered_button(window)) cursorShape = GLFW_POINTER_CURSOR;
} break;
case CSD_titlebar: if (update_hovered_button(window)) cursorShape = GLFW_POINTER_CURSOR; break;
case CSD_shadow_top: cursorShape = GLFW_N_RESIZE_CURSOR; break;
case CSD_shadow_bottom: cursorShape = GLFW_S_RESIZE_CURSOR; break;
case CSD_shadow_left: cursorShape = GLFW_W_RESIZE_CURSOR; break;
@@ -794,7 +799,6 @@ handle_pointer_enter(_GLFWwindow *window, struct wl_surface *surface) {
all_surfaces(Q)
#undef Q
decs.focus = CENTRAL_WINDOW;
decs.dragging = false;
}
static void
@@ -822,7 +826,9 @@ handle_pointer_button(_GLFWwindow *window, uint32_t button, uint32_t state) {
decs.maximize.hovered = false; decs.titlebar_needs_update = true;
} else if (decs.close.hovered) _glfwInputWindowCloseRequest(window);
}
decs.dragging = !has_hovered_button(window);
if (!has_hovered_button(window)) {
if (window->wl.xdg.toplevel) xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, _glfw.wl.pointer_serial);
}
break;
case CSD_shadow_left: edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; break;
case CSD_shadow_upper_left: edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; break;

13
glfw/wl_init.c vendored
View File

@@ -44,8 +44,6 @@
#include <sys/socket.h>
#include <wayland-client.h>
#include <stdio.h>
// errno.h needed for BSD code paths
#include <errno.h>
// Needed for the BTN_* defines
#ifdef __has_include
#if __has_include(<linux/input.h>)
@@ -599,9 +597,6 @@ static void registryHandleGlobal(void* data UNUSED,
_glfw.wl.zwlr_layer_shell_v1 = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version);
}
}
else if (is(zwp_idle_inhibit_manager_v1)) {
_glfw.wl.idle_inhibit_manager = wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1);
}
#undef is
}
@@ -636,13 +631,12 @@ static const struct wl_registry_listener registryListener = {
};
GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialized) {
return glfw_current_system_color_theme(query_if_unintialized);
GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(void) {
return glfw_current_system_color_theme();
}
static pid_t
get_socket_peer_pid(int fd) {
(void)fd;
#ifdef __linux__
struct ucred ucred;
socklen_t len = sizeof(struct ucred);
@@ -682,7 +676,6 @@ get_compositor_missing_capabilities(void) {
C(blur, org_kde_kwin_blur_manager); C(server_side_decorations, decorationManager);
C(cursor_shape, wp_cursor_shape_manager_v1); C(layer_shell, zwlr_layer_shell_v1);
C(single_pixel_buffer, wp_single_pixel_buffer_manager_v1); C(preferred_scale, has_preferred_buffer_scale);
C(idle_inhibit, idle_inhibit_manager);
#undef C
return buf;
}
@@ -865,8 +858,6 @@ void _glfwPlatformTerminate(void)
org_kde_kwin_blur_manager_destroy(_glfw.wl.org_kde_kwin_blur_manager);
if (_glfw.wl.zwlr_layer_shell_v1)
zwlr_layer_shell_v1_destroy(_glfw.wl.zwlr_layer_shell_v1);
if (_glfw.wl.idle_inhibit_manager)
zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idle_inhibit_manager);
if (_glfw.wl.registry)
wl_registry_destroy(_glfw.wl.registry);

21
glfw/wl_monitor.c vendored
View File

@@ -31,6 +31,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
@@ -89,6 +90,7 @@ static void outputHandleDone(void* data, struct wl_output* output UNUSED)
for (int i = 0; i < _glfw.monitorCount; i++) {
if (_glfw.monitors[i] == monitor) return;
}
_glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST);
}
@@ -101,28 +103,11 @@ static void outputHandleScale(void* data,
monitor->wl.scale = factor;
}
static void outputHandleName(void* data,
struct wl_output* output UNUSED,
const char* name) {
struct _GLFWmonitor *monitor = data;
if (name) strncpy(monitor->wl.friendly_name, name, sizeof(monitor->wl.friendly_name)-1);
}
static void outputHandleDescription(void* data,
struct wl_output* output UNUSED,
const char* description) {
struct _GLFWmonitor *monitor = data;
if (description) strncpy(monitor->wl.description, description, sizeof(monitor->wl.description)-1);
}
static const struct wl_output_listener outputListener = {
outputHandleGeometry,
outputHandleMode,
outputHandleDone,
outputHandleScale,
outputHandleName,
outputHandleDescription,
};
@@ -148,7 +133,7 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version)
output = wl_registry_bind(_glfw.wl.registry,
name,
&wl_output_interface,
MIN(version, (unsigned)WL_OUTPUT_NAME_SINCE_VERSION));
2);
if (!output)
{
_glfwFreeMonitor(monitor);

7
glfw/wl_platform.h vendored
View File

@@ -65,7 +65,6 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#include "wayland-kwin-blur-v1-client-protocol.h"
#include "wayland-wlr-layer-shell-unstable-v1-client-protocol.h"
#include "wayland-single-pixel-buffer-v1-client-protocol.h"
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
#define _glfw_dlclose(handle) dlclose(handle)
@@ -221,7 +220,7 @@ typedef struct _GLFWwindowWayland
} pointerLock;
struct {
bool serverSide, buffer_destroyed, titlebar_needs_update, dragging;
bool serverSide, buffer_destroyed, titlebar_needs_update;
_GLFWCSDSurface focus;
_GLFWWaylandCSDSurface titlebar, shadow_left, shadow_right, shadow_top, shadow_bottom, shadow_upper_left, shadow_upper_right, shadow_lower_left, shadow_lower_right;
@@ -233,7 +232,7 @@ typedef struct _GLFWwindowWayland
struct {
int width, height;
bool focused;
bool focused, needs_shadow;
double fscale;
WaylandWindowState toplevel_states;
} for_window_state;
@@ -339,7 +338,6 @@ typedef struct _GLFWlibraryWayland
struct org_kde_kwin_blur_manager *org_kde_kwin_blur_manager;
struct zwlr_layer_shell_v1* zwlr_layer_shell_v1; uint32_t zwlr_layer_shell_v1_version;
struct wp_single_pixel_buffer_manager_v1 *wp_single_pixel_buffer_manager_v1;
struct zwp_idle_inhibit_manager_v1* idle_inhibit_manager;
int compositorVersion;
int seatVersion;
@@ -397,7 +395,6 @@ typedef struct _GLFWmonitorWayland
{
struct wl_output* output;
uint32_t name;
char friendly_name[64], description[64];
int currentMode;
int x;

113
glfw/wl_window.c vendored
View File

@@ -251,7 +251,8 @@ setCursorImage(_GLFWwindow* window, bool on_theme_change) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: late cursor load failed; proceeding with existing cursor");
}
}
if (!cursorWayland->cursor || !cursorWayland->cursor->image_count || !cursorWayland->cursor->images) return;
if (!cursorWayland->cursor || !cursorWayland->cursor->image_count)
return;
if (cursorWayland->currentImage >= cursorWayland->cursor->image_count) cursorWayland->currentImage = 0;
image = cursorWayland->cursor->images[cursorWayland->currentImage];
if (!image) image = cursorWayland->cursor->images[0];
@@ -370,8 +371,8 @@ wait_for_swap_to_commit(_GLFWwindow *window) {
static void
resizeFramebuffer(_GLFWwindow* window) {
GLFWwindow *ctx = glfwGetCurrentContext();
bool ctx_changed = false;
if (ctx != (GLFWwindow*)window && window->context.client != GLFW_NO_API) { ctx_changed = true; glfwMakeContextCurrent((GLFWwindow*)window); }
const bool ctx_changed = ctx != (GLFWwindow*)window;
if (ctx_changed) glfwMakeContextCurrent((GLFWwindow*)window);
double scale = _glfwWaylandWindowScale(window);
int scaled_width = (int)round(window->wl.width * scale);
int scaled_height = (int)round(window->wl.height * scale);
@@ -823,7 +824,7 @@ create_single_color_buffer(int width, int height, pixel color) {
if (width == 1 && height == 1 && _glfw.wl.wp_single_pixel_buffer_manager_v1) {
#define C(x) (uint32_t)(((double)((uint64_t)color.alpha * color.x * UINT32_MAX)) / (255 * 255))
struct wl_buffer *ans = wp_single_pixel_buffer_manager_v1_create_u32_rgba_buffer(
_glfw.wl.wp_single_pixel_buffer_manager_v1, C(red), C(green), C(blue), (uint32_t)((color.alpha / 255.) * UINT32_MAX));
_glfw.wl.wp_single_pixel_buffer_manager_v1, C(red), C(green), C(blue), color.alpha * UINT32_MAX);
#undef C
if (!ans) _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: failed to create single pixel buffer");
return ans;
@@ -952,10 +953,14 @@ void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled UNUSED) {
static struct wl_output*
find_output_by_name(const char* name) {
if (!name || !name[0]) return NULL;
for (int i = 0; i < _glfw.monitorCount; i++) {
_GLFWmonitor *m = _glfw.monitors[i];
if (strcmp(m->wl.friendly_name, name) == 0) return m->wl.output;
if (!name) return NULL;
int count;
GLFWmonitor** monitors = glfwGetMonitors(&count);
for (int i = 0; i < count; ++i) {
_GLFWmonitor *m = (_GLFWmonitor*)monitors + i;
if (strcmp(m->name, name) == 0) {
return m->wl.output;
}
}
return NULL;
}
@@ -963,7 +968,7 @@ find_output_by_name(const char* name) {
static void
layer_set_properties(_GLFWwindow *window) {
enum zwlr_layer_surface_v1_anchor which_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
int exclusive_zone = window->wl.layer_shell.config.requested_exclusive_zone;
int exclusive_zone = -1;
enum zwlr_layer_surface_v1_keyboard_interactivity focus_policy = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
switch(window->wl.layer_shell.config.focus_policy) {
case GLFW_FOCUS_NOT_ALLOWED: focus_policy = ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE; break;
@@ -972,36 +977,28 @@ layer_set_properties(_GLFWwindow *window) {
}
int panel_width = 0, panel_height = 0;
switch (window->wl.layer_shell.config.type) {
case GLFW_LAYER_SHELL_NONE: break;
case GLFW_LAYER_SHELL_BACKGROUND: exclusive_zone = -1; break;
case GLFW_LAYER_SHELL_TOP:
case GLFW_LAYER_SHELL_OVERLAY:
case GLFW_LAYER_SHELL_BACKGROUND: break; case GLFW_LAYER_SHELL_NONE: break;
case GLFW_LAYER_SHELL_PANEL:
switch (window->wl.layer_shell.config.edge) {
case GLFW_EDGE_TOP:
which_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
panel_height = window->wl.height;
if (!window->wl.layer_shell.config.override_exclusive_zone) exclusive_zone = window->wl.height;
exclusive_zone = window->wl.height;
break;
case GLFW_EDGE_BOTTOM:
which_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
panel_height = window->wl.height;
if (!window->wl.layer_shell.config.override_exclusive_zone) exclusive_zone = window->wl.height;
exclusive_zone = window->wl.height;
break;
case GLFW_EDGE_LEFT:
which_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
panel_width = window->wl.width;
if (!window->wl.layer_shell.config.override_exclusive_zone) exclusive_zone = window->wl.width;
exclusive_zone = window->wl.width;
break;
case GLFW_EDGE_RIGHT:
which_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
panel_width = window->wl.width;
if (!window->wl.layer_shell.config.override_exclusive_zone) exclusive_zone = window->wl.width;
break;
case GLFW_EDGE_NONE:
which_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; // None is anchored to "top left"
panel_width = window->wl.width;
panel_height = window->wl.height;
exclusive_zone = window->wl.width;
break;
}
}
@@ -1011,7 +1008,7 @@ layer_set_properties(_GLFWwindow *window) {
debug("Compositor will be informed that layer size: %dx%d viewport: %dx%d at next surface commit\n", panel_width, panel_height, window->wl.width, window->wl.height);
zwlr_layer_surface_v1_set_anchor(surface, which_anchor);
zwlr_layer_surface_v1_set_exclusive_zone(surface, exclusive_zone);
zwlr_layer_surface_v1_set_margin(surface, window->wl.layer_shell.config.requested_top_margin, window->wl.layer_shell.config.requested_right_margin, window->wl.layer_shell.config.requested_bottom_margin, window->wl.layer_shell.config.requested_left_margin);
zwlr_layer_surface_v1_set_margin(surface, 0, 0, 0, 0);
zwlr_layer_surface_v1_set_keyboard_interactivity(surface, focus_policy);
#undef surface
}
@@ -1064,13 +1061,8 @@ create_layer_shell_surface(_GLFWwindow *window) {
}
window->decorated = false; // shell windows must not have decorations
struct wl_output *wl_output = find_output_by_name(window->wl.layer_shell.config.output_name);
enum zwlr_layer_shell_v1_layer which_layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND; // Default to background
switch (window->wl.layer_shell.config.type) {
case GLFW_LAYER_SHELL_BACKGROUND: break; case GLFW_LAYER_SHELL_NONE: break;
case GLFW_LAYER_SHELL_PANEL: which_layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM; break;
case GLFW_LAYER_SHELL_TOP: which_layer = ZWLR_LAYER_SHELL_V1_LAYER_TOP; break;
case GLFW_LAYER_SHELL_OVERLAY: which_layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; break;
}
enum zwlr_layer_shell_v1_layer which_layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
if (window->wl.layer_shell.config.type == GLFW_LAYER_SHELL_PANEL) which_layer = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM;
#define ls window->wl.layer_shell.zwlr_layer_surface_v1
ls = zwlr_layer_shell_v1_get_layer_surface(
_glfw.wl.zwlr_layer_shell_v1, window->wl.surface, wl_output, which_layer, "kitty");
@@ -1332,8 +1324,30 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
window->swaps_disallowed = true;
if (!createSurface(window, wndconfig)) return false;
if (wndconfig->title) window->wl.title = _glfw_strdup(wndconfig->title);
if (wndconfig->maximized) window->wl.maximize_on_first_show = true;
if (ctxconfig->client != GLFW_NO_API)
{
if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||
ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
{
if (!_glfwInitEGL())
return false;
if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
return false;
}
else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
{
if (!_glfwInitOSMesa())
return false;
if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
return false;
}
}
if (wndconfig->title)
window->wl.title = _glfw_strdup(wndconfig->title);
if (wndconfig->maximized)
window->wl.maximize_on_first_show = true;
if (wndconfig->visible)
{
@@ -1356,29 +1370,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));
window->wl.monitorsCount = 0;
window->wl.monitorsSize = 1;
// looping till window fully created attaches a single pixel buffer to the window,
// this cannot be done once a OpenGL context is created for the window. So first loop
// and only then create the OpenGL context.
if (window->wl.visible) loop_till_window_fully_created(window);
debug("Creating OpenGL context and attaching it to window\n");
if (ctxconfig->client != GLFW_NO_API)
{
if (ctxconfig->source == GLFW_EGL_CONTEXT_API ||
ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
{
if (!_glfwInitEGL())
return false;
if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
return false;
}
else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
{
if (!_glfwInitOSMesa())
return false;
if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
return false;
}
}
return true;
}
@@ -1407,6 +1399,7 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
wp_viewport_destroy(window->wl.wp_viewport);
if (window->wl.org_kde_kwin_blur)
org_kde_kwin_blur_release(window->wl.org_kde_kwin_blur);
if (window->wl.layer_shell.config.output_name) free((void*)window->wl.layer_shell.config.output_name);
if (window->context.destroy)
window->context.destroy(window);
@@ -1610,6 +1603,7 @@ void _glfwPlatformShowWindow(_GLFWwindow* window)
if (!window->wl.visible) {
create_window_desktop_surface(window);
window->wl.visible = true;
loop_till_window_fully_created(window);
}
}
@@ -2731,8 +2725,8 @@ GLFWAPI void glfwRequestWaylandFrameEvent(GLFWwindow *handle, unsigned long long
}
}
GLFWAPI unsigned long long glfwDBusUserNotify(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *data) {
return glfw_dbus_send_user_notification(n, callback, data);
GLFWAPI unsigned long long glfwDBusUserNotify(const char *app_name, const char* icon, const char *summary, const char *body, const char *action_name, int32_t timeout, int urgency, GLFWDBusnotificationcreatedfun callback, void *data) {
return glfw_dbus_send_user_notification(app_name, icon, summary, body, action_name, timeout, urgency, callback, data);
}
GLFWAPI void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler) {
@@ -2753,8 +2747,11 @@ GLFWAPI void glfwWaylandRedrawCSDWindowTitle(GLFWwindow *handle) {
if (csd_change_title(window)) commit_window_surface_if_safe(window);
}
GLFWAPI void glfwWaylandSetupLayerShellForNextWindow(const GLFWLayerShellConfig *c) {
layer_shell_config_for_next_window = *c;
GLFWAPI void glfwWaylandSetupLayerShellForNextWindow(GLFWLayerShellConfig c) {
if (layer_shell_config_for_next_window.output_name) free((void*)layer_shell_config_for_next_window.output_name);
layer_shell_config_for_next_window = c;
if (layer_shell_config_for_next_window.output_name && !layer_shell_config_for_next_window.output_name[0]) layer_shell_config_for_next_window.output_name = NULL;
if (layer_shell_config_for_next_window.output_name) layer_shell_config_for_next_window.output_name = strdup(layer_shell_config_for_next_window.output_name);
}
void

6
glfw/x11_init.c vendored
View File

@@ -30,7 +30,6 @@
#define _GNU_SOURCE
#include "internal.h"
#include "backend_utils.h"
#include "linux_desktop_settings.h"
#include <X11/Xresource.h>
@@ -615,8 +614,8 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot)
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialized) {
return glfw_current_system_color_theme(query_if_unintialized);
GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(void) {
return GLFW_COLOR_SCHEME_NO_PREFERENCE;
}
void _glfwPlatformInputColorScheme(GLFWColorScheme appearance UNUSED) { }
@@ -649,7 +648,6 @@ int _glfwPlatformInit(void)
"X11: Failed to initialize event loop data");
}
glfw_dbus_init(&_glfw.x11.dbus, &_glfw.x11.eventLoopData);
glfw_initialize_desktop_settings(); // needed for color scheme change notification
_glfw.x11.screen = DefaultScreen(_glfw.x11.display);
_glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen);

4
glfw/x11_window.c vendored
View File

@@ -3253,8 +3253,8 @@ GLFWAPI int glfwGetNativeKeyForName(const char* keyName, bool caseSensitive) {
return glfw_xkb_keysym_from_name(keyName, caseSensitive);
}
GLFWAPI unsigned long long glfwDBusUserNotify(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *data) {
return glfw_dbus_send_user_notification(n, callback, data);
GLFWAPI unsigned long long glfwDBusUserNotify(const char *app_name, const char* icon, const char *summary, const char *body, const char *action_name, int32_t timeout, int urgency,GLFWDBusnotificationcreatedfun callback, void *data) {
return glfw_dbus_send_user_notification(app_name, icon, summary, body, action_name, timeout, urgency, callback, data);
}
GLFWAPI void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler) {

12
go.mod
View File

@@ -1,22 +1,22 @@
module kitty
go 1.23
go 1.22
require (
github.com/ALTree/bigfloat v0.2.0
github.com/alecthomas/chroma/v2 v2.14.0
github.com/bmatcuk/doublestar/v4 v4.7.1
github.com/dlclark/regexp2 v1.11.4
github.com/bmatcuk/doublestar/v4 v4.6.1
github.com/dlclark/regexp2 v1.11.0
github.com/edwvee/exiffix v0.0.0-20240229113213-0dbb146775be
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.6.0
github.com/kovidgoyal/imaging v1.6.3
github.com/seancfoley/ipaddress-go v1.7.0
github.com/seancfoley/ipaddress-go v1.6.0
github.com/shirou/gopsutil/v3 v3.24.5
github.com/zeebo/xxh3 v1.0.2
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b
golang.org/x/image v0.23.0
golang.org/x/sys v0.28.0
golang.org/x/image v0.17.0
golang.org/x/sys v0.21.0
howett.net/plist v1.0.1
)

20
go.sum
View File

@@ -6,14 +6,14 @@ github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/edwvee/exiffix v0.0.0-20240229113213-0dbb146775be h1:FNPYI8/ifKGW7kdBdlogyGGaPXZmOXBbV1uz4Amr3s0=
github.com/edwvee/exiffix v0.0.0-20240229113213-0dbb146775be/go.mod h1:G3dK5MziX9e4jUa8PWjowCOPCcyQwxsZ5a0oYA73280=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
@@ -40,8 +40,8 @@ github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/seancfoley/bintree v1.3.1 h1:cqmmQK7Jm4aw8gna0bP+huu5leVOgHGSJBEpUx3EXGI=
github.com/seancfoley/bintree v1.3.1/go.mod h1:hIUabL8OFYyFVTQ6azeajbopogQc2l5C/hiXMcemWNU=
github.com/seancfoley/ipaddress-go v1.7.0 h1:vWp3SR3k+HkV3aKiNO2vEe6xbVxS0x/Ixw6hgyP238s=
github.com/seancfoley/ipaddress-go v1.7.0/go.mod h1:TQRZgv+9jdvzHmKoPGBMxyiaVmoI0rYpfEk8Q/sL/Iw=
github.com/seancfoley/ipaddress-go v1.6.0 h1:9z7yGmOnV4P2ML/dlR/kCJiv5tp8iHOOetJvxJh/R5w=
github.com/seancfoley/ipaddress-go v1.6.0/go.mod h1:TQRZgv+9jdvzHmKoPGBMxyiaVmoI0rYpfEk8Q/sL/Iw=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
@@ -63,15 +63,15 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI=
golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
golang.org/x/image v0.17.0 h1:nTRVVdajgB8zCMZVsViyzhnMKPwYeroEERRC64JuLco=
golang.org/x/image v0.17.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=

View File

@@ -150,7 +150,7 @@ func GetChoices(o *Options) (response string, err error) {
ctx := style.Context{AllowEscapeCodes: true}
draw_choice_boxes := func(y, screen_width, _ int, choices ...Choice) {
draw_choice_boxes := func(y, screen_width, screen_height int, choices ...Choice) {
clickable_ranges = map[string][]Range{}
width := screen_width - 2
current_line_length := 0
@@ -402,7 +402,7 @@ func GetChoices(o *Options) (response string, err error) {
if ev.MatchesPressOrRepeat("esc") || ev.MatchesPressOrRepeat("ctrl+c") {
ev.Handled = true
lp.Quit(1)
} else if ev.MatchesPressOrRepeat("enter") || ev.MatchesPressOrRepeat("kp_enter") {
} else if ev.MatchesPressOrRepeat("enter") {
ev.Handled = true
response = response_on_accept
lp.Quit(0)

View File

@@ -65,9 +65,6 @@ func (k *kitty_font_backend_type) start() (err error) {
var kitty_font_backend kitty_font_backend_type
func (k *kitty_font_backend_type) send(v any) error {
if k.to == nil {
return fmt.Errorf("Trying to send data when to pipe is nil")
}
data, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("Could not encode message to kitty with error: %w", err)

View File

@@ -48,12 +48,10 @@ def setup_debug_print() -> bool:
def send_to_kitten(x: Any) -> None:
f = sys.__stdout__
assert f is not None
try:
f.buffer.write(json.dumps(x).encode())
f.buffer.write(b'\n')
f.buffer.flush()
sys.__stdout__.buffer.write(json.dumps(x).encode())
sys.__stdout__.buffer.write(b'\n')
sys.__stdout__.buffer.flush()
except BrokenPipeError:
raise SystemExit('Pipe to kitten was broken while sending data to it')
@@ -124,7 +122,7 @@ def get_features(features: Dict[str, Optional['FeatureData']]) -> Dict[str, FD]:
return ans
def render_face_sample(font: Descriptor, opts: Options, dpi_x: float, dpi_y: float, width: int, height: int, sample_text: str = '') -> RenderedSample:
def render_face_sample(font: Descriptor, opts: Options, dpi_x: float, dpi_y: float, width: int, height: int) -> RenderedSample:
face = face_from_descriptor(font, opts.font_size, dpi_x, dpi_y)
face.set_size(opts.font_size, dpi_x, dpi_y)
metadata = {
@@ -141,7 +139,7 @@ def render_face_sample(font: Descriptor, opts: Options, dpi_x: float, dpi_y: flo
if ns:
metadata['variable_named_style'] = ns
metadata['variable_axis_map'] = get_axis_map(face)
bitmap, cell_width, cell_height = face.render_sample_text(sample_text or SAMPLE_TEXT, width, height, opts.foreground.rgb)
bitmap, cell_width, cell_height = face.render_sample_text(SAMPLE_TEXT, width, height, opts.foreground.rgb)
metadata['cell_width'] = cell_width
metadata['cell_height'] = cell_height
metadata['canvas_height'] = len(bitmap) // (4 *width)
@@ -232,7 +230,7 @@ def query_kitty() -> Dict[str, str]:
return ans
def showcase(family: str = 'family="Fira Code"', sample_text: str = '') -> None:
def showcase(family: str = 'family="Fira Code"') -> None:
q = query_kitty()
opts = Options()
opts.foreground = to_color(q['foreground'])
@@ -244,7 +242,7 @@ def showcase(family: str = 'family="Fira Code"', sample_text: str = '') -> None:
ss = screen_size_function()()
width = ss.cell_width * ss.cols
height = 5 * ss.cell_height
bitmap, m = render_face_sample(desc, opts, float(q['dpi_x']), float(q['dpi_y']), width, height, sample_text=sample_text)
bitmap, m = render_face_sample(desc, opts, float(q['dpi_x']), float(q['dpi_y']), width, height)
display_bitmap(bitmap, m['canvas_width'], m['canvas_height'])

View File

@@ -35,7 +35,7 @@ func (self *final_pane) draw_screen() (err error) {
"",
"What would you like to do?",
"",
fmt.Sprintf("%s to modify %s and use the new fonts", h("Enter"), s("italic", self.handler.opts.Config_file_name)),
fmt.Sprintf("%s to modify %s and use the new fonts", h("Enter"), s("italic", `kitty.conf`)),
"",
fmt.Sprintf("%s to abort and return to font selection", h("Esc")),
"",
@@ -78,12 +78,7 @@ func (self *final_pane) on_key_event(event *loop.KeyEvent) (err error) {
if event.MatchesPressOrRepeat("enter") {
event.Handled = true
patcher := config.Patcher{Write_backup: true}
path := ""
if filepath.IsAbs(self.handler.opts.Config_file_name) {
path = self.handler.opts.Config_file_name
} else {
path = filepath.Join(utils.ConfigDir(), self.handler.opts.Config_file_name)
}
path := filepath.Join(utils.ConfigDir(), "kitty.conf")
updated, err := patcher.Patch(path, "KITTY_FONTS", self.settings.serialized(), "font_family", "bold_font", "italic_font", "bold_italic_font")
if err != nil {
return err

View File

@@ -68,8 +68,7 @@ func main(opts *Options) (rc int, err error) {
}
type Options struct {
Reload_in string
Config_file_name string
Reload_in string
}
func EntryPoint(root *cli.Command) {
@@ -94,19 +93,7 @@ func EntryPoint(root *cli.Command) {
running in to reload its config, after making changes. Use this option to
instead either not reload the config at all or in all running kitty instances.`,
})
ans.Add(cli.OptionSpec{
Name: "--config-file-name",
Dest: "Config_file_name",
Type: "str",
Default: "kitty.conf",
Help: `The name or path to the config file to edit. Relative paths are interpreted
with respect to the kitty config directory. By default the kitty config
file, kitty.conf is edited. This is most useful if you add include
fonts.conf to your kitty.conf and then have the kitten operate only on
fonts.conf, allowing kitty.conf to remain unchanged.`,
})
clone := root.AddClone(ans.Group, ans)
clone.Hidden = true
clone.Hidden = false
clone.Name = "choose_fonts"
}

View File

@@ -1,6 +0,0 @@
if __name__ == '__main__':
import os
import sys
from kitty.constants import kitten_exe
os.execlp(kitten_exe(), 'kitten', *sys.argv)

View File

@@ -110,7 +110,7 @@ func run_plain_text_loop(opts *Options) (err error) {
defer tempfile.Close()
}
}
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
if err != nil {
return
}
@@ -139,24 +139,19 @@ func run_plain_text_loop(opts *Options) (err error) {
buf := make([]byte, 8192)
write_one_chunk := func() error {
orig := enc_writer.last_written_id
for enc_writer.last_written_id == orig {
n, err := data_src.Read(buf[:cap(buf)])
if n > 0 {
enc.Write(buf[:n])
}
if err == nil {
continue
}
if errors.Is(err, io.EOF) {
enc.Close()
send_to_loop("\x1b\\")
after_read_from_stdin()
return nil
}
n, err := data_src.Read(buf[:cap(buf)])
if err != nil && !errors.Is(err, io.EOF) {
send_to_loop("\x1b\\")
return err
}
if n > 0 {
enc.Write(buf[:n])
}
if errors.Is(err, io.EOF) {
enc.Close()
send_to_loop("\x1b\\")
after_read_from_stdin()
}
return nil
}

View File

@@ -203,11 +203,7 @@ func unescape_metadata_value(k, x string) (ans string) {
func encode_bytes(metadata map[string]string, payload []byte) string {
ans := strings.Builder{}
enc_payload := ""
if len(payload) > 0 {
enc_payload = base64.StdEncoding.EncodeToString(payload)
}
ans.Grow(2048 + len(enc_payload))
ans.Grow(2048)
ans.WriteString("\x1b]")
ans.WriteString(OSC_NUMBER)
ans.WriteString(";")
@@ -221,7 +217,7 @@ func encode_bytes(metadata map[string]string, payload []byte) string {
}
if len(payload) > 0 {
ans.WriteString(";")
ans.WriteString(enc_payload)
ans.WriteString(base64.StdEncoding.EncodeToString(payload))
}
ans.WriteString("\x1b\\")
return ans.String()
@@ -286,7 +282,7 @@ func parse_aliases(raw []string) (map[string][]string, error) {
}
func run_get_loop(opts *Options, args []string) (err error) {
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
if err != nil {
return err
}

View File

@@ -46,7 +46,7 @@ func (self *Input) has_mime_matching(predicate func(string) bool) bool {
}
func write_loop(inputs []*Input, opts *Options) (err error) {
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
if err != nil {
return err
}
@@ -222,6 +222,8 @@ func run_set_loop(opts *Options, args []string) (err error) {
return fmt.Errorf("Could not guess MIME type for %s use the --mime option to specify a MIME type", arg)
}
to_process[i] = inputs[i]
if to_process[i].is_stream {
}
}
return write_loop(to_process, opts)
}

View File

@@ -58,7 +58,7 @@ func get_ssh_file(hostname, rpath string) (string, error) {
for strings.HasPrefix(rpath, "/") {
rpath = rpath[1:]
}
cmd := []string{ssh.SSHExe(), hostname, "tar", "--dereference", "--create", "--file", "-"}
cmd := []string{ssh.SSHExe(), hostname, "tar", "-c", "-f", "-"}
if is_abs {
cmd = append(cmd, "-C", "/")
}

View File

@@ -221,14 +221,6 @@ map('Scroll to previous change',
'prev_change p scroll_to prev-change',
)
map('Scroll to next file',
'next_file shift+j scroll_to next-file',
)
map('Scroll to previous file',
'prev_file shift+k scroll_to prev-file',
)
map('Show all context',
'all_context a change_context all',
)

View File

@@ -69,7 +69,6 @@ func (self *line_pos) Equal(other tui.LinePos) bool {
}
return false
}
func (self *line_pos) LessThan(other tui.LinePos) bool {
if o, ok := other.(*line_pos); ok {
return self.y.Less(o.y)
@@ -114,6 +113,7 @@ func (self *Handler) drag_scroll_tick(timer_id loop.IdType) error {
}
var debugprintln = tty.DebugPrintln
var _ = debugprintln
func (self *Handler) update_mouse_selection(ev *loop.MouseEvent) {
if !self.mouse_selection.IsActive() {
@@ -146,12 +146,6 @@ func (self *Handler) text_for_current_mouse_selection() string {
}
text := make([]byte, 0, 2048)
start_pos, end_pos := *self.mouse_selection.StartLine().(*line_pos), *self.mouse_selection.EndLine().(*line_pos)
// if start is after end, swap them
if end_pos.y.Less(start_pos.y) {
start_pos, end_pos = end_pos, start_pos
}
start, end := start_pos.y, end_pos.y
is_left := start_pos.min_x == self.logical_lines.margin_size

View File

@@ -184,6 +184,7 @@ func (self *Handler) highlight_all() {
self.async_results <- r
self.lp.WakeupMainThread()
}()
}
func (self *Handler) load_all_images() {
@@ -522,27 +523,6 @@ func (self *Handler) scroll_to_next_change(backwards bool) bool {
return false
}
func (self *Handler) scroll_to_next_file(backwards bool) bool {
if backwards {
for i := self.scroll_pos.logical_line - 1; i >= 0; i-- {
line := self.logical_lines.At(i)
if line.line_type == TITLE_LINE {
self.scroll_pos = ScrollPos{i, 0}
return true
}
}
} else {
for i := self.scroll_pos.logical_line + 1; i < self.logical_lines.Len(); i++ {
line := self.logical_lines.At(i)
if line.line_type == TITLE_LINE {
self.scroll_pos = ScrollPos{i, 0}
return true
}
}
}
return false
}
func (self *Handler) scroll_to_next_match(backwards, include_current_match bool) bool {
if self.current_search == nil {
return false
@@ -636,8 +616,6 @@ func (self *Handler) dispatch_action(name, args string) error {
case `scroll_to`:
done := false
switch {
case strings.Contains(args, "file"):
done = self.scroll_to_next_file(strings.Contains(args, `prev`))
case strings.Contains(args, `change`):
done = self.scroll_to_next_change(strings.Contains(args, `prev`))
case strings.Contains(args, `match`):

View File

@@ -217,11 +217,11 @@ func main(_ *cli.Command, o *Options, args []string) (rc int, err error) {
}
lp.OnInitialize = func() (string, error) {
lp.SendOverlayReady()
lp.SetCursorVisible(false)
lp.SetWindowTitle(window_title)
lp.AllowLineWrapping(false)
draw_screen()
lp.SendOverlayReady()
return "", nil
}
lp.OnFinalize = func() string {

View File

@@ -427,28 +427,6 @@ func specialize_command(hg *cli.Command) {
hg.ArgCompleter = cli.CompletionForWrapper("rg")
}
type Options struct {
}
func create_cmd(root *cli.Command, run_func func(*cli.Command, *Options, []string) (int, error)) {
ans := root.AddSubCommand(&cli.Command{
Name: "hyperlinked_grep",
Run: func(cmd *cli.Command, args []string) (int, error) {
opts := Options{}
err := cmd.GetOptionValues(&opts)
if err != nil {
return 1, err
}
return run_func(cmd, &opts, args)
},
Hidden: true,
})
specialize_command(ans)
clone := root.AddClone(ans.Group, ans)
clone.Hidden = false
clone.Name = "hyperlinked-grep"
}
func EntryPoint(parent *cli.Command) {
create_cmd(parent, main)
}

View File

@@ -21,7 +21,7 @@ func DetectSupport(timeout time.Duration) (memory, files, direct bool, err error
temp_files_to_delete := make([]string, 0, 8)
shm_files_to_delete := make([]shm.MMap, 0, 8)
var direct_query_id, file_query_id, memory_query_id uint32
lp, e := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
lp, e := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
if e != nil {
err = e
return

View File

@@ -292,7 +292,7 @@ func main(cmd *cli.Command, o *Options, args []string) (rc int, err error) {
if imgd.err != nil {
print_error("Failed to process \x1b[31m%s\x1b[39m: %s\r\n", imgd.source_name, imgd.err)
} else {
transmit_image(imgd, opts.NoTrailingNewline)
transmit_image(imgd)
if imgd.err != nil {
print_error("Failed to transmit \x1b[31m%s\x1b[39m: %s\r\n", imgd.source_name, imgd.err)
}

View File

@@ -133,7 +133,7 @@ Use the Unicode placeholder method to display the images. Useful to display
images from within full screen terminal programs that do not understand the
kitty graphics protocol such as multiplexers or editors. See
:ref:`graphics_unicode_placeholders` for details. Note that when using this
method, images placed (with :option:`--place`) that do not fit on the screen,
method, placed (with :option:`--place`) images that do not fit on the screen,
will get wrapped at the screen edge instead of getting truncated. This
wrapping is per line and therefore the image will look like it is interleaved
with blank lines.
@@ -155,12 +155,6 @@ default=0
The graphics protocol id to use for the created image. Normally, a random id is created if needed.
This option allows control of the id. When multiple images are sent, sequential ids starting from the specified id
are used. Valid ids are from 1 to 4294967295. Numbers outside this range are automatically wrapped.
--no-trailing-newline -n
type=bool-set
By default, the cursor is moved to the next line after displaying an image. This option, prevents that. Should not be used
when catting multiple images. Also has no effect when the :option:`--place` option is used.
'''
help_text = (

View File

@@ -272,15 +272,13 @@ func write_unicode_placeholder(imgd *image_data) {
for c := 0; c < imgd.width_cells; c++ {
os.Stdout.WriteString(string(kitty.ImagePlaceholderChar) + string(images.NumberToDiacritic[r]) + string(images.NumberToDiacritic[c]) + id_char)
}
if r < imgd.height_cells-1 {
os.Stdout.WriteString("\n\r")
}
os.Stdout.WriteString("\n\r")
}
}
var seen_image_ids *utils.Set[uint32]
func transmit_image(imgd *image_data, no_trailing_newline bool) {
func transmit_image(imgd *image_data) {
if seen_image_ids == nil {
seen_image_ids = utils.NewSet[uint32](32)
}
@@ -408,7 +406,7 @@ func transmit_image(imgd *image_data, no_trailing_newline bool) {
return
}
}
if imgd.move_to.x == 0 && !no_trailing_newline {
if imgd.move_to.x == 0 {
fmt.Println() // ensure cursor is on new line
}
}

View File

@@ -1,352 +0,0 @@
package notify
import (
"bytes"
"encoding/base64"
"fmt"
"image"
"io"
"os"
"slices"
"strconv"
"strings"
"time"
"kitty/tools/cli"
"kitty/tools/tty"
"kitty/tools/tui/loop"
"kitty/tools/utils"
)
var _ = fmt.Print
const ESC_CODE_PREFIX = "\x1b]99;"
const ESC_CODE_SUFFIX = "\x1b\\"
const CHUNK_SIZE = 4096
func b64encode(x string) string {
return base64.RawStdEncoding.EncodeToString(utils.UnsafeStringToBytes(x))
}
func check_id_valid(x string) bool {
pat := utils.MustCompile(`[^a-zA-Z0-9_+.-]`)
return pat.ReplaceAllString(x, "") == x
}
type parsed_data struct {
opts *Options
wait_till_closed bool
expire_time time.Duration
title, body, identifier string
image_data []byte
initial_msg string
}
func (p *parsed_data) create_metadata() string {
ans := []string{}
if p.opts.AppName != "" {
ans = append(ans, "f="+b64encode(p.opts.AppName))
}
switch p.opts.Urgency {
case "low":
ans = append(ans, "u=0")
case "critical":
ans = append(ans, "u=2")
}
if p.expire_time >= 0 {
ans = append(ans, "w="+strconv.FormatInt(p.expire_time.Milliseconds(), 10))
}
if p.opts.Type != "" {
ans = append(ans, "t="+b64encode(p.opts.Type))
}
if p.wait_till_closed {
ans = append(ans, "c=1:a=report")
}
for _, x := range p.opts.Icon {
ans = append(ans, "n="+b64encode(x))
}
if p.opts.IconCacheId != "" {
ans = append(ans, "g="+p.opts.IconCacheId)
}
if p.opts.SoundName != "system" {
ans = append(ans, "s="+b64encode(p.opts.SoundName))
}
m := strings.Join(ans, ":")
if m != "" {
m = ":" + m
}
return m
}
var debugprintln = tty.DebugPrintln
func (p *parsed_data) generate_chunks(callback func(string)) {
prefix := ESC_CODE_PREFIX + "i=" + p.identifier
write_chunk := func(middle string) {
callback(prefix + middle + ESC_CODE_SUFFIX)
}
add_payload := func(payload_type, payload string) {
if payload == "" {
return
}
p := utils.IfElse(payload_type == "title", "", ":p="+payload_type)
payload = b64encode(payload)
for len(payload) > 0 {
chunk := payload[:min(CHUNK_SIZE, len(payload))]
payload = utils.IfElse(len(payload) > len(chunk), payload[len(chunk):], "")
write_chunk(":d=0:e=1" + p + ";" + chunk)
}
}
metadata := p.create_metadata()
write_chunk(":d=0" + metadata + ";")
add_payload("title", p.title)
add_payload("body", p.body)
if len(p.image_data) > 0 {
add_payload("icon", utils.UnsafeBytesToString(p.image_data))
}
if len(p.opts.Button) > 0 {
add_payload("buttons", strings.Join(p.opts.Button, "\u2028"))
}
write_chunk(";")
}
func (p *parsed_data) run_loop() (err error) {
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
if err != nil {
return err
}
activated := -1
prefix := ESC_CODE_PREFIX + "i=" + p.identifier
poll_for_close := func() {
lp.AddTimer(time.Millisecond*50, false, func(_ loop.IdType) error {
lp.QueueWriteString(prefix + ":p=alive;" + ESC_CODE_SUFFIX)
return nil
})
}
lp.OnInitialize = func() (string, error) {
if p.initial_msg != "" {
return p.initial_msg, nil
}
p.generate_chunks(func(x string) { lp.QueueWriteString(x) })
return "", nil
}
lp.OnEscapeCode = func(ect loop.EscapeCodeType, data []byte) error {
if ect == loop.OSC && bytes.HasPrefix(data, []byte(ESC_CODE_PREFIX[2:])) {
raw := utils.UnsafeBytesToString(data[len(ESC_CODE_PREFIX[2:]):])
metadata, payload, _ := strings.Cut(raw, ";")
sent_identifier, payload_type := "", ""
for _, x := range strings.Split(metadata, ":") {
key, val, _ := strings.Cut(x, "=")
switch key {
case "i":
sent_identifier = val
case "p":
payload_type = val
}
}
if sent_identifier == p.identifier {
switch payload_type {
case "close":
if payload == "untracked" {
poll_for_close()
} else {
lp.Quit(0)
}
case "alive":
live_ids := strings.Split(payload, ",")
if slices.Contains(live_ids, p.identifier) {
poll_for_close()
} else {
lp.Quit(0)
}
case "":
if activated, err = strconv.Atoi(utils.IfElse(payload == "", "0", payload)); err != nil {
return fmt.Errorf("Got invalid activation response from terminal: %#v", payload)
}
}
}
}
return nil
}
close_requested := 0
lp.OnKeyEvent = func(event *loop.KeyEvent) error {
if event.MatchesPressOrRepeat("ctrl+c") || event.MatchesPressOrRepeat("esc") {
event.Handled = true
switch close_requested {
case 0:
lp.QueueWriteString(prefix + ":p=close;" + ESC_CODE_SUFFIX)
lp.Println("Closing notification, please wait...")
close_requested++
case 1:
key := "Esc"
if event.MatchesPressOrRepeat("ctrl+c") {
key = "Ctrl+C"
}
lp.Println(fmt.Sprintf("Waiting for response from terminal, press the %s key again to abort. Note that this might result in garbage being printed to the terminal.", key))
close_requested++
default:
return fmt.Errorf("Aborted by user!")
}
}
return nil
}
err = lp.Run()
ds := lp.DeathSignalName()
if ds != "" {
fmt.Println("Killed by signal: ", ds)
lp.KillIfSignalled()
return
}
if activated > -1 && err == nil {
fmt.Println(activated)
}
return
}
func random_ident() (string, error) {
return utils.HumanUUID4()
}
func parse_duration(x string) (ans time.Duration, err error) {
switch x {
case "never":
return 0, nil
case "":
return -1, nil
}
trailer := x[len(x)-1]
multipler := time.Second
switch trailer {
case 's':
x = x[:len(x)-1]
case 'm':
x = x[:len(x)-1]
multipler = time.Minute
case 'h':
x = x[:len(x)-1]
multipler = time.Hour
case 'd':
x = x[:len(x)-1]
multipler = time.Hour * 24
}
val, err := strconv.ParseFloat(x, 64)
if err != nil {
return ans, err
}
ans = time.Duration(float64(multipler) * val)
return
}
func (p *parsed_data) load_image_data() (err error) {
if p.opts.IconPath == "" {
return nil
}
f, err := os.Open(p.opts.IconPath)
if err != nil {
return err
}
defer f.Close()
_, imgfmt, err := image.DecodeConfig(f)
if _, err = f.Seek(0, io.SeekStart); err != nil {
return err
}
if err == nil && imgfmt != "" && strings.Contains("jpeg jpg gif png", strings.ToLower(imgfmt)) {
p.image_data, err = io.ReadAll(f)
return
}
return fmt.Errorf("The icon must be in PNG, JPEG or GIF formats")
}
func main(_ *cli.Command, opts *Options, args []string) (rc int, err error) {
if len(args) == 0 {
return 1, fmt.Errorf("Must specify a TITLE for the notification")
}
var p parsed_data
p.opts = opts
p.title = args[0]
if len(args) > 1 {
p.body = strings.Join(args[1:], " ")
}
ident := opts.Identifier
if ident == "" {
if ident, err = random_ident(); err != nil {
return 1, fmt.Errorf("Failed to generate a random identifier with error: %w", err)
}
}
bad_ident := func(which string) error {
return fmt.Errorf("Invalid identifier: %s must be only English letters, numbers, hyphens and underscores.", which)
}
if !check_id_valid(ident) {
return 1, bad_ident(ident)
}
p.identifier = ident
if !check_id_valid(opts.IconCacheId) {
return 1, bad_ident(opts.IconCacheId)
}
if len(p.title) == 0 {
if ident == "" {
return 1, fmt.Errorf("Must specify a non-empty TITLE for the notification or specify an identifier to close a notification.")
}
msg := ESC_CODE_PREFIX + "i=" + ident + ":p=close;" + ESC_CODE_SUFFIX
if opts.OnlyPrintEscapeCode {
_, err = os.Stdout.WriteString(msg)
} else if p.wait_till_closed {
p.initial_msg = msg
err = p.run_loop()
} else {
var term *tty.Term
if term, err = tty.OpenControllingTerm(); err != nil {
return 1, fmt.Errorf("Failed to open controlling terminal with error: %w", err)
}
if _, err = term.WriteString(msg); err != nil {
term.RestoreAndClose()
return 1, err
}
term.RestoreAndClose()
}
}
if p.expire_time, err = parse_duration(opts.ExpireAfter); err != nil {
return 1, fmt.Errorf("Invalid expire time: %s with error: %w", opts.ExpireAfter, err)
}
p.wait_till_closed = opts.WaitTillClosed
if err = p.load_image_data(); err != nil {
return 1, fmt.Errorf("Failed to load image data from %s with error %w", opts.IconPath, err)
}
if opts.OnlyPrintEscapeCode {
p.generate_chunks(func(x string) {
if err == nil {
_, err = os.Stdout.WriteString(x)
}
})
} else {
if opts.PrintIdentifier {
fmt.Println(ident)
}
if p.wait_till_closed {
err = p.run_loop()
} else {
var term *tty.Term
if term, err = tty.OpenControllingTerm(); err != nil {
return 1, fmt.Errorf("Failed to open controlling terminal with error: %w", err)
}
p.generate_chunks(func(x string) {
if err == nil {
_, err = term.WriteString(x)
}
})
term.RestoreAndClose()
}
}
if err != nil {
rc = 1
}
return
}
func EntryPoint(parent *cli.Command) {
create_cmd(parent, main)
}

View File

@@ -1,120 +0,0 @@
#!/usr/bin/env python
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import sys
def OPTIONS() -> str:
from kitty.constants import standard_icon_names
return f'''
--icon -n
type=list
The name of the icon to use for the notification. An icon with this name
will be searched for on the computer running the terminal emulator. Can
be specified multiple times, the first name that is found will be used.
Standard names: {', '.join(sorted(standard_icon_names))}
--icon-path -p
Path to an image file in PNG/JPEG/GIF formats to use as the icon. If both
name and path are specified then first the name will be looked for and if not found
then the path will be used.
--app-name -a
default=kitten-notify
The application name for the notification.
--button -b
type=list
Add a button with the specified text to the notification. Can be specified multiple times for multiple buttons.
If --wait-till-closed is used then the kitten will print the button number to STDOUT if the user clicks a button.
1 for the first button, 2 for the second button and so on.
--urgency -u
default=normal
choices=normal,low,critical
The urgency of the notification.
--expire-after -e
The duration, for the notification to appear on screen. The default is to
use the policy of the OS notification service. A value of :code:`never` means the notification should
never expire, however, this may or may not work depending on the policies of the OS notification
service. Time is specified in the form NUMBER[SUFFIX] where SUFFIX can be :code:`s` for seconds, :code:`m` for minutes,
:code:`h` for hours or :code:`d` for days. Non-integer numbers are allowed.
If not specified, seconds is assumed. The notification is guaranteed to be closed automatically
after the specified time has elapsed. The notification could be closed before by user
action or OS policy.
--sound-name -s
default=system
The name of the sound to play with the notification. :code:`system` means let the
notification system use whatever sound it wants. :code:`silent` means prevent
any sound from being played. Any other value is passed to the desktop's notification system
which may or may not honor it.
--type -t
The notification type. Can be any string, it is used by users to create filter rules
for notifications, so choose something descriptive of the notification's purpose.
--identifier -i
The identifier of this notification. If a notification with the same identifier
is already displayed, it is replaced/updated.
--print-identifier -P
type=bool-set
Print the identifier for the notification to STDOUT. Useful when not specifying
your own identifier via the --identifier option.
--wait-till-closed --wait-for-completion -w
type=bool-set
Wait until the notification is closed. If the user activates the notification,
"0" is printed to STDOUT before quitting. If a button on the notification is pressed the
number corresponding to the button is printed to STDOUT. Press the Esc or Ctrl+C keys
to close the notification manually.
--only-print-escape-code
type=bool-set
Only print the escape code to STDOUT. Useful if using this kitten as part
of a larger application. If this is specified, the --wait-till-closed option
will be used for escape code generation, but no actual waiting will be done.
--icon-cache-id -g
Identifier to use when caching icons in the terminal emulator. Using an identifier means
that icon data needs to be transmitted only once using --icon-path. Subsequent invocations
will use the cached icon data, at least until the terminal instance is restarted. This is useful
if this kitten is being used inside a larger application, with --only-print-escape-code.
'''
help_text = '''\
Send notifications to the user that are displayed to them via the
desktop environment's notifications service. Works over SSH as well.
To update an existing notification, specify the identifier of the notification
with the --identifier option. The value should be the same as the identifier specified for
the notification you wish to update.
If no title is specified and an identifier is specified using the --identifier
option, then instead of creating a new notification, an existing notification
with the specified identifier is closed.
'''
usage = 'TITLE [BODY ...]'
if __name__ == '__main__':
raise SystemExit('This should be run as `kitten notify ...`')
elif __name__ == '__doc__':
cd = sys.cli_docs # type: ignore
cd['usage'] = usage
cd['options'] = OPTIONS
cd['help_text'] = help_text
cd['short_desc'] = 'Send notifications to the user'

View File

@@ -10,16 +10,10 @@ from kitty.constants import appname, is_macos, is_wayland
from kitty.fast_data_types import (
GLFW_EDGE_BOTTOM,
GLFW_EDGE_LEFT,
GLFW_EDGE_NONE,
GLFW_EDGE_RIGHT,
GLFW_EDGE_TOP,
GLFW_FOCUS_EXCLUSIVE,
GLFW_FOCUS_NOT_ALLOWED,
GLFW_FOCUS_ON_DEMAND,
GLFW_LAYER_SHELL_BACKGROUND,
GLFW_LAYER_SHELL_OVERLAY,
GLFW_LAYER_SHELL_PANEL,
GLFW_LAYER_SHELL_TOP,
glfw_primary_monitor_size,
make_x11_window_a_dock_window,
)
@@ -28,48 +22,14 @@ from kitty.types import LayerShellConfig
from kitty.typing import EdgeLiteral
OPTIONS = r'''
--lines
--lines --columns
type=int
default=1
The number of lines shown in the panel. Ignored for background and vertical panels.
--columns
type=int
default=1
The number of columns shown in the panel. Ignored for background and horizontal panels.
--margin-top
type=int
default=0
Request a given top margin to the compositor.
Only works on a Wayland compositor that supports the wlr layer shell protocol.
--margin-left
type=int
default=0
Request a given left margin to the compositor.
Only works on a Wayland compositor that supports the wlr layer shell protocol.
--margin-bottom
type=int
default=0
Request a given bottom margin to the compositor.
Only works on a Wayland compositor that supports the wlr layer shell protocol.
--margin-right
type=int
default=0
Request a given right margin to the compositor.
Only works on a Wayland compositor that supports the wlr layer shell protocol.
The number of lines shown in the panel if horizontal otherwise the number of columns shown in the panel. Ignored for background panels.
--edge
choices=top,bottom,left,right,background,none
choices=top,bottom,left,right,background
default=top
Which edge of the screen to place the panel on. Note that some window managers
(such as i3) do not support placing docked windows on the left and right edges.
@@ -77,16 +37,6 @@ The value :code:`background` means make the panel the "desktop wallpaper". This
is only supported on Wayland, not X11 and note that when using sway if you set
a background in your sway config it will cover the background drawn using this
kitten.
The value :code:`none` anchors the panel to the top left corner by default
and the panel should be placed using margins parameters.
--layer
choices=background,bottom,top,overlay
default=bottom
On a Wayland compositor that supports the wlr layer shell protocol, specifies the layer
on which the panel should be drawn. This parameter is ignored and set to
:code:`background` if :option:`--edge` is set to :code:`background`.
--config -c
@@ -118,29 +68,6 @@ condition=not is_macos
Set the name part of the :italic:`WM_CLASS` property (defaults to using the value from :option:`{appname} --class`)
--focus-policy
choices=not-allowed,exclusive,on-demand
default=not-allowed
On a Wayland compositor that supports the wlr layer shell protocol, specify the focus policy for keyboard
interactivity with the panel. Please refer to the wlr layer shell protocol documentation for more details.
--exclusive-zone
type=int
default=-1
On a Wayland compositor that supports the wlr layer shell protocol, request a given exclusive zone for the panel.
Please refer to the wlr layer shell documentation for more details on the meaning of exclusive and its value.
If :option:`--edge` is set to anything else than :code:`none`, this flag will not have any effect unless
the flag :option:`--override-exclusive-zone` is also set.
If :option:`--edge` is set to :code:`background`, this option has no effect.
--override-exclusive-zone
type=bool-set
On a Wayland compositor that supports the wlr layer shell protocol, override the default exclusive zone.
This has effect only if :option:`--edge` is set to :code:`top`, :code:`left`, :code:`bottom` or :code:`right`.
--debug-rendering
type=bool-set
For internal debugging use.
@@ -223,29 +150,9 @@ def initial_window_size_func(opts: WindowSizeData, cached_values: Dict[str, Any]
def layer_shell_config(opts: PanelCLIOptions) -> LayerShellConfig:
ltype = {'background': GLFW_LAYER_SHELL_BACKGROUND,
'bottom': GLFW_LAYER_SHELL_PANEL,
'top': GLFW_LAYER_SHELL_TOP,
'overlay': GLFW_LAYER_SHELL_OVERLAY}.get(opts.layer, GLFW_LAYER_SHELL_PANEL)
ltype = GLFW_LAYER_SHELL_BACKGROUND if opts.edge == 'background' else ltype
edge = {
'top': GLFW_EDGE_TOP, 'bottom': GLFW_EDGE_BOTTOM, 'left': GLFW_EDGE_LEFT, 'right': GLFW_EDGE_RIGHT, 'none': GLFW_EDGE_NONE
}.get(opts.edge, GLFW_EDGE_TOP)
focus_policy = {
'not-allowed': GLFW_FOCUS_NOT_ALLOWED, 'exclusive': GLFW_FOCUS_EXCLUSIVE, 'on-demand': GLFW_FOCUS_ON_DEMAND
}.get(opts.focus_policy, GLFW_FOCUS_NOT_ALLOWED)
return LayerShellConfig(type=ltype,
edge=edge,
x_size_in_cells=max(1, opts.columns),
y_size_in_cells=max(1, opts.lines),
requested_top_margin=max(0, opts.margin_top),
requested_left_margin=max(0, opts.margin_left),
requested_bottom_margin=max(0, opts.margin_bottom),
requested_right_margin=max(0, opts.margin_right),
focus_policy=focus_policy,
requested_exclusive_zone=opts.exclusive_zone,
override_exclusive_zone=opts.override_exclusive_zone,
output_name=opts.output_name or '')
ltype = GLFW_LAYER_SHELL_BACKGROUND if opts.edge == 'background' else GLFW_LAYER_SHELL_PANEL
edge = {'top': GLFW_EDGE_TOP, 'bottom': GLFW_EDGE_BOTTOM, 'left': GLFW_EDGE_LEFT, 'right': GLFW_EDGE_RIGHT}.get(opts.edge, GLFW_EDGE_TOP)
return LayerShellConfig(type=ltype, edge=edge, size_in_cells=max(1, opts.lines), output_name=opts.output_name or '')
def main(sys_args: List[str]) -> None:

View File

@@ -26,7 +26,7 @@ func main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {
queries[i] = x
}
}
lp, err := loop.New(loop.NoAlternateScreen, loop.NoKeyboardStateChange, loop.NoMouseTracking, loop.NoRestoreColors, loop.NoInBandResizeNotifications)
lp, err := loop.New(loop.NoAlternateScreen, loop.NoKeyboardStateChange, loop.NoMouseTracking, loop.NoRestoreColors)
if err != nil {
return 1, err
}

View File

@@ -179,12 +179,14 @@ class Foreground(Query):
@staticmethod
def get_result(opts: Options, window_id: int, os_window_id: int) -> str:
from kitty.fast_data_types import get_boss, get_options
from kitty.fast_data_types import Color, get_boss
boss = get_boss()
w = boss.window_id_map.get(window_id)
if w is None:
return opts.foreground.as_sharp
return (w.screen.color_profile.default_fg or get_options().foreground).as_sharp
col = w.screen.color_profile.default_fg
r, g, b = col >> 16, (col >> 8) & 0xff, col & 0xff
return Color(r, g, b).as_sharp
@query
@@ -194,27 +196,16 @@ class Background(Query):
@staticmethod
def get_result(opts: Options, window_id: int, os_window_id: int) -> str:
from kitty.fast_data_types import get_boss, get_options
from kitty.fast_data_types import Color, get_boss
boss = get_boss()
w = boss.window_id_map.get(window_id)
if w is None:
return opts.background.as_sharp
return (w.screen.color_profile.default_bg or get_options().background).as_sharp
col = w.screen.color_profile.default_bg
r, g, b = col >> 16, (col >> 8) & 0xff, col & 0xff
return Color(r, g, b).as_sharp
@query
class BackgroundOpacity(Query):
name: str = 'background_opacity'
help_text: str = 'The current background opacity as a number between 0 and 1'
@staticmethod
def get_result(opts: Options, window_id: int, os_window_id: int) -> str:
from kitty.fast_data_types import background_opacity_of
ans = background_opacity_of(os_window_id)
if ans is None:
ans = 1.0
return f'{ans:g}'
@query
class ClipboardControl(Query):

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