Compare commits

...

1364 Commits

Author SHA1 Message Date
Kovid Goyal
5bdf27fd78 version 0.26.2 2022-09-05 10:49:50 +05:30
Kovid Goyal
f05783e64d Sanitize notifications ids as they are retransmitted over the TTY 2022-09-05 10:41:19 +05:30
Kovid Goyal
c455fea729 Also output screen contents when test fails 2022-09-05 07:52:36 +05:30
Kovid Goyal
5bf5454dbe Nicer fix for #5469 2022-09-04 22:15:17 +05:30
Kovid Goyal
b60d9f73ce Wayland: Fix remembering window size not accurate when client side decorations are present 2022-09-04 14:25:04 +05:30
Kovid Goyal
31fe35cd38 Fix #5469 2022-09-04 13:58:16 +05:30
Kovid Goyal
607bc845fc Allow creating overlay-main windows, which are treated as the active window unlike normal overlays
Fixes #5392
2022-09-03 19:04:31 +05:30
Kovid Goyal
a420d9d19c spacing is a clone safe option 2022-09-02 23:28:58 +05:30
Kovid Goyal
4b81e4936e launch: Allow setting the margin and padding for the newly created window
Fixes #5463
2022-09-02 19:41:05 +05:30
Kovid Goyal
295743a96e Fix ref to launch doc 2022-09-02 16:31:21 +05:30
Kovid Goyal
d3a1848c27 Remove markup from ref_hyperlink text 2022-09-02 16:24:50 +05:30
Kovid Goyal
9cec924f32 hints kitten: Allow using launch as the program to run, to open the result in a new kitty tab/window/etc.
Fixes #5462
2022-09-02 16:23:00 +05:30
Kovid Goyal
67c963ab89 Pass KITTY_LISTEN_ON to programs opened via open_url
Useful if those programs want to talk to kitty via the rc socket
2022-09-02 16:01:30 +05:30
Kovid Goyal
0f7ced25c1 ... 2022-09-01 13:55:48 +05:30
Kovid Goyal
42948ffff0 Merge branch 'docs' of https://github.com/page-down/kitty 2022-09-01 13:55:30 +05:30
pagedown
922ce5bdea Docs: Put example commands in code blocks 2022-09-01 15:35:51 +08:00
pagedown
1b42486026 Docs: Remove the extra empty line in the CLI help
Also keep the example code in a code block, in the HTML documentation.
2022-09-01 15:35:41 +08:00
pagedown
95f1a971b2 Docs: Mention clear_all_shortcuts in the FAQ about show_key kitten 2022-09-01 15:35:17 +08:00
Kovid Goyal
7968c88c67 ... 2022-09-01 07:54:36 +05:30
Kovid Goyal
992e90b0a3 Fix a regression in 0.26.0 that broke mapping of native keys who key codes did not fit in 21 bits
Fixes #5452
2022-08-31 22:19:51 +05:30
Kovid Goyal
afada84f52 Cleanup previous PR
Use wcswidth() for line width calculation. Works with emoji and ignores
escape codes automatically. Add changelog entry.
2022-08-31 08:01:52 +05:30
Kovid Goyal
b978ff8930 Merge branch 'fix/hints-next-line' of https://github.com/trygveaa/kitty 2022-08-31 07:56:38 +05:30
Kovid Goyal
053cede581 Use git rev-parse to get nightly commit hash 2022-08-31 07:22:56 +05:30
Trygve Aaberge
03720402a7 Fix hints sometimes matching next line as part of URL
For URLs where there's fewer characters left to the right edge of the
window than the number of escape characters in the line, the next line
would be included in the URL, as if the URL went all the way to the
right edge.

For example with a 40 chars wide terminal, if you run:

    echo -e '\e[31m1\e[m https://github.com/kovidgoyal/kitty\ntest'

And launch the hints kitten, you'll see that test on the next line will
be included in the URL.

This happened because the calculation for filling the rest of the line
with NUL characters counted the escape characters as well as the visible
characters, so it filled in too few characters.

This is a regression introduced in commit 91966712.
2022-08-30 22:24:58 +02:00
Kovid Goyal
fca0999814 Eureka! Figured out why libedit is breaking in prewarm on macOS via launchd
The prewarm zygote imports the world. shell.py had a top level import
for readline. Which means readline was being imported pre-fork. And of
course as is traditional with Apple libedit is not fork safe. Probably
because it initializes its internal IO routines based on the stdio
handles at time of import which are the handles kitty gets from launchd
2022-08-30 19:35:17 +05:30
Kovid Goyal
78056c659c Another place we cant use readline on macOS 2022-08-30 19:27:55 +05:30
Kovid Goyal
30e1b4680d Only override LANG in default env if it was not set either in the original os env or via the env config directive 2022-08-30 19:17:53 +05:30
Kovid Goyal
89a0c04d19 Fix exec in prewarm forked process sometimes inheriting env vars from grandparent process 2022-08-30 19:03:32 +05:30
Kovid Goyal
1ba027c277 Dont show LC_CTYPE in show_kitty_env_vars when running with system python on macOS 2022-08-30 16:55:17 +05:30
Kovid Goyal
d8e43a3412 macOS: Fix regression in 0.26.0 that caused asking the user for a line of input such as for set_tab_title to not work
Apparently libedit doesn't work in the forked process when the parent
process is run via Launch Services. I cant be bothered to investigate
why, given that libedit is closed source. macOS users will just have to
live without history/completion in the ask kitten until I get around to
writing a replacement for readline/libedit.

And on a personal note, macOS >> Necrotizing fasciitis

Fixes #5447
2022-08-30 16:45:14 +05:30
Kovid Goyal
644e46e91d Reconfigure all stdio streams in the prefork process 2022-08-30 16:22:59 +05:30
Kovid Goyal
e2a1f8dde7 ... 2022-08-30 10:40:59 +05:30
Kovid Goyal
7d09c6b762 Link to start of actual changelog 2022-08-30 10:27:53 +05:30
Kovid Goyal
6604e0d015 Fix regression in 0.26.0 that caused launching kitty without working STDIO handles to result in high CPU usage and prewarming failing
Fixes #5444
2022-08-30 08:15:13 +05:30
Kovid Goyal
bb2ec9cde7 ... 2022-08-30 07:50:36 +05:30
Kovid Goyal
89a2545855 Cleanup previous PR
Allow disabling hyperlinking of rg output. Also parse
--kitten=hyperlink=... form of CLI args.
2022-08-30 07:41:14 +05:30
Kovid Goyal
49f8c0eae6 Merge branch 'hyperlinked-grep-only-matches' of https://github.com/groves/kitty 2022-08-30 06:59:53 +05:30
Kovid Goyal
268d687814 version 0.26.1
Bloody macOS. I have better things to do that play whack-a-mole with all
the bug reports caused macOS not bothering to set LANG.
2022-08-30 01:04:42 +05:30
Charlie Groves
db5b4da388 Add options for linking different parts of ripgrep output 2022-08-29 13:23:42 -04:00
Kovid Goyal
71f8e50460 Update changelog 2022-08-29 21:01:59 +05:30
Kovid Goyal
22fbdbca40 Fix a regression in 0.26.0 that caused kitty to no longer set the LANG environment variable on macOS
Happened because reading the locale uses cocoa APIs and they are not fork
safe, so it was moved to after prewarm forking, but at that point the
default child env had already been set.

Fixes #5439
2022-08-29 20:58:48 +05:30
Kovid Goyal
6253ee2a74 Allow resetting the tab title to default 2022-08-29 20:41:07 +05:30
Kovid Goyal
5b28aed0b1 Allow the set_tab_title action to take an argument 2022-08-29 20:11:18 +05:30
Kovid Goyal
9eabc9ecf1 Merge branch 'bash-inherit_errexit' of https://github.com/grimm26/kovidgoyal-kitty 2022-08-29 19:47:24 +05:30
Mark Keisler
a77852466c bash shell integration: Send STDERR of unsetting inherit_errexit to
/dev/null

Some bash versions may not have that option and rather than checking if
they even have it or if it is set, just quiet the STDERR it may throw.

```
$ kssh myhost
bash: shopt: inherit_errexit: invalid shell option name
myhost:~$ bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
```
2022-08-29 08:58:54 -05:00
Kovid Goyal
c68b82e4d0 ssh kitten: Fix executable permission missing from kitty bootstrap script
Fixes #5438
2022-08-29 18:20:09 +05:30
Kovid Goyal
16a4845a72 version 0.26.0 2022-08-29 05:05:11 +05:30
Kovid Goyal
16da031f68 Wait till prompt appears before issuing clear 2022-08-28 21:54:55 +05:30
Kovid Goyal
43b2935e52 Make fish shell integration test for XDG_DATA_DIRS work even if the shells rc files add to XDG_DATA_DIRS.
My Linux distro has now started installing system wide rc files that do
this :((
2022-08-28 21:15:26 +05:30
Charlie Groves
8c7968a4d6 Describe --hyperlink-only-matches in the docs 2022-08-28 07:02:39 -04:00
Kovid Goyal
fba759a47c missed some at_ -> at- 2022-08-28 12:04:00 +05:30
Kovid Goyal
3bcb694b1b Switch over to at-cmd form for references.
Sphinx anyway converts at_cmd to at-cmd for anchors in HTML so using at-cmd
consistently.
2022-08-28 11:54:32 +05:30
Kovid Goyal
555020cfd5 Also allow using at-cmd 2022-08-28 10:26:38 +05:30
Kovid Goyal
391e2b8488 Fix resolution of anchor names for kitty @ command docs 2022-08-28 10:25:00 +05:30
Kovid Goyal
c856d5c058 Fix hyperlinks not present when fetching text from the history buffer
Fixes #5427
2022-08-28 08:54:53 +05:30
Charlie Groves
f5a70adf0b Add a --hyperlink-only-matches flag to hyperlinked_grep
If it's active, only matching lines get links, not file headers or context lines.
2022-08-26 17:05:50 -04:00
Kovid Goyal
e330c38d4a ssh kitten: Delegate to ssh when -T is used
Fixes #5411
2022-08-24 15:23:41 +05:30
Kovid Goyal
d595084395 DRYer 2022-08-24 15:02:36 +05:30
Kovid Goyal
b32ee5ee46 Forgot to change code to remove markup for github links 2022-08-24 15:00:39 +05:30
Kovid Goyal
098dd10789 ... 2022-08-24 14:42:40 +05:30
Kovid Goyal
b5fa643c5d Allow ignoring failures when mapping remote control commands 2022-08-24 14:21:54 +05:30
Kovid Goyal
664dd29970 Merge branch 'rc-no-response' of https://github.com/page-down/kitty 2022-08-24 14:15:13 +05:30
Kovid Goyal
b7a4281985 Allow ignoring failure to close windows/tabs via rc commands 2022-08-24 14:13:31 +05:30
Kovid Goyal
9e9aa52553 Ensure extract_rt_targets is run only once per build 2022-08-24 14:00:44 +05:30
Kovid Goyal
77fbdfbb53 Fix finding local docs when running from source on macOS 2022-08-24 13:54:14 +05:30
Kovid Goyal
214416f1e3 Fix handling of :doc: in CLI help output 2022-08-24 13:43:47 +05:30
pagedown
800437ec6b Remote control: Add --no-response to some commands 2022-08-24 15:14:53 +08:00
Kovid Goyal
957eafbef0 Merge branch 'docs' of https://github.com/page-down/kitty 2022-08-24 12:35:00 +05:30
pagedown
7f228bcbf5 Docs: Unify the list style of available values
Highlight the value and provide hyperlink in the description.
2022-08-24 13:30:52 +08:00
pagedown
fd4a682c5b Fix doc website URL with duplicate slashes 2022-08-24 13:29:51 +08:00
pagedown
ba9afc61a4 Hyperlink doc text role 2022-08-24 13:29:07 +08:00
pagedown
9a70709bba Shorten the reference link to the top of the page
Reduce the length of the link :ref:`shell_integration` in commented conf.
2022-08-24 13:27:19 +08:00
pagedown
c437a36794 Fix GitHub links in commented configuration
Hyperlink GitHub related text roles: iss, pull, disc
2022-08-24 13:27:12 +08:00
pagedown
0e0578ff49 Docs: Use --help long option name in the document
Remove the extra empty line in the CLI help for `--replay-commands`.
2022-08-24 13:27:06 +08:00
pagedown
64a202a125 Docs: Using the lowercase form of the key name consistently 2022-08-24 13:26:50 +08:00
Kovid Goyal
740d1ca146 Merge branch 'high-sierra-compat' of https://github.com/rkitover/kitty 2022-08-20 22:15:10 +05:30
Rafael Kitover
7b75f055ea Fix build on macOS High Sierra
Check for >= 10.14 using Availability.h to use the accessibilityRole
callback which does not seem to be available in 10.13.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2022-08-20 16:08:28 +00:00
Kovid Goyal
8d6a3cff25 Fix docs build error 2022-08-20 16:26:49 +05:30
Kovid Goyal
10fdde6647 Hyperlink :envvar: as well 2022-08-20 16:24:46 +05:30
Kovid Goyal
e1c45dc23f ... 2022-08-20 16:20:30 +05:30
Kovid Goyal
2efb8f6dc2 Create an API to easily use remote control commands from kittens 2022-08-20 16:19:16 +05:30
Kovid Goyal
3b77f3b4bc Also hyperlink :term: 2022-08-20 15:40:39 +05:30
Kovid Goyal
31f763282f Document the show help shortcut 2022-08-20 14:03:16 +05:30
Kovid Goyal
39434fb1f6 A new default shortcut to show the kitty documentation 2022-08-20 13:55:10 +05:30
Kovid Goyal
44ccdd36d6 Remove socket prewarming
The potential for breakage is too high, and I am working on an
alternative solution that will be better long term.

Prewarming is still used for kittens launched via keybindings
2022-08-20 13:38:33 +05:30
Kovid Goyal
4cc0138a28 Delete the frozen python dir
I verified that all the files remaining in it are loaded via the python
resource system and so are not needed.
2022-08-20 13:05:12 +05:30
Kovid Goyal
6ca39d9d83 Move code to delete empty folders into bypy
So it applies to both linux and mac builds automatically
2022-08-20 12:46:55 +05:30
Kovid Goyal
26c099639c Remove empty folders from macOS package builds 2022-08-20 12:20:18 +05:30
Kovid Goyal
329043ba08 Cleanup build documentation
Note how to build the kitty docs wherever needed instead of in a
separate section
2022-08-20 12:09:36 +05:30
Kovid Goyal
e289f4959f DRYer 2022-08-20 12:03:33 +05:30
Kovid Goyal
b6760a59fa Another bug in resolve_ref 2022-08-20 11:45:48 +05:30
Kovid Goyal
cd35f92607 Fix resolve_ref() 2022-08-20 11:29:40 +05:30
Kovid Goyal
ea8f223298 Cleanup the wrap implementation
Make it more efficient and add support for hyperlink escapes
2022-08-20 09:17:05 +05:30
Kovid Goyal
a4c3154dab ... 2022-08-20 08:27:24 +05:30
Kovid Goyal
6aa3405c78 Clean up build docs a bit 2022-08-20 08:13:42 +05:30
Kovid Goyal
da618584af Fix some outdated docs 2022-08-19 15:48:17 +05:30
Kovid Goyal
9bc31a5651 Use locally built docs when running from source if they exist 2022-08-19 15:01:20 +05:30
Kovid Goyal
314dd97059 Command line tools shouldnt need to resolve documentation refs
Instead use a new kitty+doc protocol and have kitty navigate to the
docs. Uses a default open action for the protocol, which can be
overriden by the user should they so desire.
2022-08-19 14:35:17 +05:30
Kovid Goyal
0b66f20934 Fix mapping of ref targets with underscores 2022-08-19 14:34:25 +05:30
Kovid Goyal
aba634ce88 Fix building of docs on macOS without warnings
--detach doesnt exist on macOS
2022-08-19 14:16:28 +05:30
Kovid Goyal
033950dae9 Only hyperlink when stdout is a tty 2022-08-19 14:05:53 +05:30
Kovid Goyal
a59e1adee1 Also hyperlink the :ac: role in CLI help output 2022-08-19 14:05:11 +05:30
Kovid Goyal
5350eb29c1 A new mappable action to show kitty docs in the browser 2022-08-19 14:00:30 +05:30
Kovid Goyal
2bb42e67d7 We have to build docs to test building of package now 2022-08-19 13:51:35 +05:30
Kovid Goyal
8745c4cd4a Make the kitty man pages automatically available inside kitty when using a binary build 2022-08-19 12:59:10 +05:30
Kovid Goyal
3288400005 Make the docs and man pages available in the macos bundle as well 2022-08-19 12:38:10 +05:30
Kovid Goyal
1f499a7c6c Add newline to end of generated file 2022-08-19 11:29:47 +05:30
Kovid Goyal
012731c3be Clean _generated.h files 2022-08-19 11:22:30 +05:30
Kovid Goyal
5deed81737 Dont maintain ref_map manually 2022-08-19 11:20:50 +05:30
Kovid Goyal
6f6e23bf61 Update docs 2022-08-19 10:00:06 +05:30
Kovid Goyal
d2d0487356 Hyperlink :ref: roles in CLI help 2022-08-19 09:57:52 +05:30
Kovid Goyal
30df4e5de2 Improve formatting of --stdin-source 2022-08-19 09:34:52 +05:30
Kovid Goyal
3f4588c408 Improve formatting of launch --type help message 2022-08-19 09:30:22 +05:30
Kovid Goyal
4e4ca366d9 Merge branch 'docs' of https://github.com/page-down/kitty 2022-08-18 17:52:24 +05:30
pagedown
040ff0cc9d For running the new code KITTY_PREWARM_SOCKET needs to be dropped 2022-08-18 17:06:07 +08:00
pagedown
b01ad4bb3b ... 2022-08-18 17:05:35 +08:00
pagedown
4476251b76 List the available data names for tab_title_template in commented conf
List syntax starting with * becomes a block of text in the commented
configuration file.
2022-08-18 17:05:24 +08:00
pagedown
0f4cb1955f Use the plus sign to connect shortcut keys consistently 2022-08-18 17:05:00 +08:00
Kovid Goyal
c161ee7b5e Merge branch 'rc-password' of https://github.com/page-down/kitty 2022-08-17 18:41:48 +05:30
pagedown
a5bb2548a6 Do not set KITTY_LISTEN_ON after the window rc password is configured 2022-08-17 19:37:52 +08:00
Kovid Goyal
2898a3ef8c Some documentation cleanup 2022-08-17 06:48:33 +05:30
Kovid Goyal
a0568334b5 Change the default for allow_remote_control back to no
That way if there are any bugs in the implementation, we wont be
exposing users by default. In the future after the implementation has
been battle tested, we can revisit.
2022-08-17 06:42:51 +05:30
Kovid Goyal
4005cfdff2 Disallow passwords that start with - to allow for future addition of options 2022-08-17 00:58:11 +05:30
Kovid Goyal
1d8c5e65b9 Ignore generated .go files 2022-08-17 00:55:12 +05:30
Kovid Goyal
56f45e8b0f Fix unhandled exceptions not being printed to stderr on macOS when run from launch services and using prewarming 2022-08-16 15:28:07 +05:30
Kovid Goyal
48a9584688 Hide the password when asking for rc permission
Can press a key or click on it to reveal it
2022-08-16 14:07:36 +05:30
Kovid Goyal
bfcd324993 ask kitten: allow having hidden text in the message 2022-08-16 13:57:58 +05:30
Kovid Goyal
a75235a260 rcp ars should be a sequnce not a set 2022-08-16 12:10:15 +05:30
Kovid Goyal
e8de2def96 Fix --allow-remote-control not working with allow_remote_control=no 2022-08-16 11:42:39 +05:30
Kovid Goyal
5ffc4c6498 DRYer 2022-08-16 11:37:31 +05:30
Kovid Goyal
d0c50248ea Graphics protocol: Only delete temp files if they have the string tty-graphics-protocol in their file paths.
This prevents deletion of arbitrary files in /tmp via the graphics
protocol.
2022-08-16 11:25:33 +05:30
Kovid Goyal
ca2a121696 Nicer error when listen on socket is not found 2022-08-16 10:59:51 +05:30
Kovid Goyal
02d06d6726 Fix --allow-remote-control without passwords not working 2022-08-16 10:51:54 +05:30
Kovid Goyal
18bf76d49e Dont set KITTY_LISTEN_ON for windows with --allow-remote-control 2022-08-16 10:50:17 +05:30
Kovid Goyal
6961845167 ask kitten: fix button click area offset vertically 2022-08-16 10:35:51 +05:30
Kovid Goyal
4c5f7f50f7 Control KITTY_LISTEN_ON more precisely 2022-08-16 10:16:18 +05:30
Kovid Goyal
572e920466 Allow restricting the remote control actions in specific windows 2022-08-16 07:08:51 +05:30
Kovid Goyal
b81fb3c865 Disable pre-warming when running gen-config.py 2022-08-16 05:41:46 +05:30
Kovid Goyal
814dd8a275 allow_remote_control should be per window not per child 2022-08-15 21:32:14 +05:30
Kovid Goyal
1619687d1d Allow specifying allowed actions when no password is sent 2022-08-15 21:05:53 +05:30
Kovid Goyal
65fc61a507 Remove unused code 2022-08-15 20:54:17 +05:30
Kovid Goyal
d027f524ce Make allow_remote_control a little more fine grained
Also, only respect listen_on if there is a possiblility of rc commands
over it being accepted
2022-08-15 20:51:11 +05:30
Kovid Goyal
df5e6e1563 simpler ignore pattern 2022-08-15 20:15:30 +05:30
Kovid Goyal
5541e3c2ff More robust cleaning of launcher dir 2022-08-14 23:39:26 +05:30
Kovid Goyal
57dfc379ae Fix cocoa being loaded before the fork on macOS 2022-08-14 22:17:27 +05:30
Kovid Goyal
b5467b8e26 Set the cocoa uncaught exception handler during glfw init not at module import time 2022-08-14 19:33:32 +05:30
Kovid Goyal
a9f1928913 ... 2022-08-14 19:16:47 +05:30
Kovid Goyal
ae16fed39b Fix repeated make app failing on macOS 2022-08-14 19:01:58 +05:30
Kovid Goyal
848a795d26 Fix minimal macos bundle build 2022-08-14 13:48:45 +05:30
Kovid Goyal
deb8c3dacd Dont put the launcher source files in the root directory 2022-08-14 13:01:29 +05:30
Kovid Goyal
3cb572b47a Fix build on older gcc 2022-08-14 10:12:17 +05:30
Kovid Goyal
df3846fbb8 Add instructions on how to install the man pages under desktop integration 2022-08-12 23:33:06 +05:30
Kovid Goyal
d73ceb26ae Use the RFC as the reference for base-85 encoding 2022-08-12 15:38:28 +05:30
Kovid Goyal
da9ffc9b93 Allow using IV and tags longer than the minimum lengths 2022-08-12 13:21:57 +05:30
Kovid Goyal
63fa0c4e94 Nicer fix for human_repr of keys defined with kitty_mod 2022-08-12 12:37:15 +05:30
Kovid Goyal
ce91b6d9d9 Switch to using the new SingleKey implementation 2022-08-12 12:21:06 +05:30
Kovid Goyal
c982f24faf Add some tests for hashing and __eq__ 2022-08-12 12:02:22 +05:30
Kovid Goyal
9f865adb83 DRYer 2022-08-12 11:39:26 +05:30
Kovid Goyal
b54dd1cb48 Increase the max key num that is storeable 2022-08-12 11:37:54 +05:30
Kovid Goyal
ef621aa099 DRYer
Also dont store negative values for key. Unset is 0 not -1
2022-08-12 11:35:45 +05:30
Kovid Goyal
df4738f080 ... 2022-08-12 11:24:18 +05:30
Kovid Goyal
8d0a06325c Update config types for new value of KITTY_MOD 2022-08-12 11:07:11 +05:30
Kovid Goyal
f3329fcd34 Fix storage for GLFW_MOD_KITTY 2022-08-12 09:58:50 +05:30
Kovid Goyal
af48547d8b Implement SingleKey._replace 2022-08-12 09:40:58 +05:30
Kovid Goyal
f228f8368a A faster version of SingleKey 2022-08-12 09:17:42 +05:30
Kovid Goyal
9bb2c1a27b Type objects should be static 2022-08-12 08:32:11 +05:30
Kovid Goyal
aa0b07a90d Fix #5371 2022-08-12 08:15:25 +05:30
Kovid Goyal
d4075bfa9a ... 2022-08-12 07:01:40 +05:30
Kovid Goyal
2b7455e353 Use kitty_mod in debug config output
Fixes #5367
2022-08-11 23:45:23 +05:30
Kovid Goyal
f42d2c63a6 Add type annotations to rc protocol field definitions 2022-08-11 22:14:33 +05:30
Kovid Goyal
6be3ae9efc Start work on standalone remote control script 2022-08-11 21:05:04 +05:30
Kovid Goyal
2fd2e07342 Fix #5366 2022-08-11 15:47:06 +05:30
Kovid Goyal
1e0e32e282 DRYer 2022-08-11 13:42:38 +05:30
Kovid Goyal
b2d8f24499 Clean up the text a bit 2022-08-11 07:47:15 +05:30
Kovid Goyal
12c99741a0 Add an example of restricting the launch command 2022-08-11 07:35:18 +05:30
Kovid Goyal
91dbcf4df0 Link to encryption spec 2022-08-11 07:29:56 +05:30
Kovid Goyal
6ebd82a84c Note which version remote_control_password was added 2022-08-11 06:52:37 +05:30
Kovid Goyal
c07178f43a Avoid transmitting the currently un-needed enc_proto 2022-08-11 06:48:35 +05:30
Kovid Goyal
78a5957863 Highlight the JSON blocks appropriately 2022-08-11 06:46:42 +05:30
Kovid Goyal
cd6882d21e ... 2022-08-11 06:40:44 +05:30
Kovid Goyal
926a2828ab 5 minutes does not actually prevent replay just minimise it 2022-08-11 06:38:59 +05:30
Kovid Goyal
daa7df96d1 ... 2022-08-11 06:38:04 +05:30
Kovid Goyal
84ca83ef69 Document the encryption used by remote_control_password 2022-08-11 06:35:25 +05:30
Kovid Goyal
ac9c85e783 Add a note about password auth over SSH 2022-08-11 06:08:28 +05:30
Kovid Goyal
331f6d4903 Document remote_control_password more thoroughly
Also ignore failures in custom auth functions
2022-08-11 05:58:02 +05:30
Kovid Goyal
2e422e5ba8 Use getpass() to read the password from STDIN when it is a tty 2022-08-11 05:20:05 +05:30
Kovid Goyal
20cffc728e Close tty_fd after dup2 2022-08-11 05:10:15 +05:30
Kovid Goyal
b06cbc9465 Use the OS secure random number generator rather than the OpenSSL one to generate IVs
The OpenSSL one is not fork safe, and in general I trust kernel CSRNG
more than OpenSSL
2022-08-11 05:07:38 +05:30
Kovid Goyal
1348327414 Pressing esc for the permission dialog should be same as reject this request 2022-08-10 20:36:15 +05:30
Kovid Goyal
cd3eeb7a95 Center choice message correctly when it is multi-line
Also fix button wrap calculation. It was not taking the borders into
account.
2022-08-10 20:34:25 +05:30
Kovid Goyal
7be6b28011 Preserve blanks lines and per line formatting in the ask kitten for the message with choices 2022-08-10 20:10:17 +05:30
Kovid Goyal
ef1bee0f48 Shorten text 2022-08-10 20:00:20 +05:30
Kovid Goyal
702da542dd Use a as the default accelerator rather than t 2022-08-10 19:11:18 +05:30
Kovid Goyal
5ba92d06cf Get async response working with sockets 2022-08-10 19:10:04 +05:30
Kovid Goyal
34a7b42063 Dont print traceback when socket is unexpectedly closed 2022-08-10 19:06:57 +05:30
Kovid Goyal
963d1b58f8 Better error message when socket is closed without response 2022-08-10 18:53:15 +05:30
Kovid Goyal
fe598bac8a Quote ^ as it is a special char in some shells 2022-08-10 17:39:05 +05:30
Kovid Goyal
45736d7f1d ... 2022-08-10 17:28:22 +05:30
Kovid Goyal
0fe4f5a686 Fix password based rc not working with generator responses 2022-08-10 17:24:10 +05:30
Kovid Goyal
e9ce5c02d0 Allow remote control commands to be interrupted by ctrl-c while waiting for a response 2022-08-10 17:00:04 +05:30
Kovid Goyal
5916d82580 Send disallowed responses when permission for rc is denied 2022-08-10 16:28:14 +05:30
Kovid Goyal
2c83b9902e Implement the permissions system for password based rc requests 2022-08-10 15:49:50 +05:30
Kovid Goyal
fe07825ad9 Work on new permissions framework for rc commands 2022-08-10 12:36:04 +05:30
Kovid Goyal
56e83d7d07 Write code to decrypt commands received in kitty 2022-08-10 12:36:04 +05:30
Kovid Goyal
c7e3c92a0a Allow listen_on to be used even without allow_remote_control
Since we intend to allow password based access, which works regardless
of the value of allow_remote_control
2022-08-10 12:36:04 +05:30
Kovid Goyal
1a643441f3 Make the kitty public key available as an env var to child processes 2022-08-10 12:36:04 +05:30
Kovid Goyal
2aee746da9 Write the code to encrypt rc messages to kitty 2022-08-10 12:36:04 +05:30
Kovid Goyal
e64b1ba67c Start work on remote control passwords 2022-08-10 12:36:04 +05:30
Kovid Goyal
31be4f041e remove unused code 2022-08-10 12:31:47 +05:30
Kovid Goyal
1f5ebc408d When searching macOS for openssl search for the same version as used by the python we are running 2022-08-10 12:30:36 +05:30
Kovid Goyal
b4d9a22df8 Install the same version of OpenSSL as used by python on brew based CI 2022-08-10 12:21:01 +05:30
Kovid Goyal
d4168e3b80 Dont add openssl to the brewfile
We only need to install it on CI because the python on CI doesnt come
from brew. Normal brewfile based installation will install openssl as a
dependency of python
2022-08-10 12:02:43 +05:30
Kovid Goyal
241c7af855 DRYer 2022-08-10 11:45:07 +05:30
Kovid Goyal
080fcd8fc1 Link against newer rather than older openssl 2022-08-10 11:36:56 +05:30
Kovid Goyal
1aad4c7ec3 Only search for brew openssl on failure 2022-08-10 11:35:43 +05:30
Kovid Goyal
e0e0582b53 Fix #5364
macOS is *the* worst UNIX I have ever seen.
2022-08-10 11:33:30 +05:30
Kovid Goyal
0a6276b007 Implement completion for kitty @ global options 2022-08-09 18:54:38 +05:30
Kovid Goyal
2a2256f7d9 ... 2022-08-09 16:11:23 +05:30
Kovid Goyal
ed81a60065 Add keywords completion for --cwd and --config 2022-08-09 16:07:04 +05:30
Kovid Goyal
5ad55dd165 Allow using file completion for any type of command line arg 2022-08-09 15:57:01 +05:30
Kovid Goyal
6860f7ffd6 fix zsh serialization of directory candidates ending with / 2022-08-09 14:51:14 +05:30
Kovid Goyal
33a3e3fb61 Add completion for kitty @ launch --logo 2022-08-09 14:30:13 +05:30
Kovid Goyal
d4df3f67b6 Allow specifying completion for command line arguments that expect paths 2022-08-09 14:21:12 +05:30
Kovid Goyal
f15a2f0c1f Change --session to resolve relative paths from the kitty config dir 2022-08-09 12:36:16 +05:30
Kovid Goyal
252288aff2 kitten completion: Fix --long-opt <TAB> not working 2022-08-09 12:02:25 +05:30
Kovid Goyal
65a20a2407 Merge branch 'show-key-kitten' of https://github.com/page-down/kitty 2022-08-09 11:32:10 +05:30
pagedown
5f180dbd9a ... 2022-08-09 12:29:22 +08:00
pagedown
ee4e9aa678 Add missing modifier key aliases 2022-08-09 12:15:55 +08:00
pagedown
9f1c9529aa Allow completion for show_key kitten
Use the lowercase form of kitty.
2022-08-09 12:15:48 +08:00
Kovid Goyal
5bba1a0851 get_current_selection should return char* not const char* 2022-08-09 09:05:13 +05:30
Kovid Goyal
e9823eb40c Cleanup previous PR 2022-08-09 08:54:47 +05:30
Kovid Goyal
14fc3f6ac4 Merge branch 'macos-accessibility' of https://github.com/page-down/kitty 2022-08-09 08:52:44 +05:30
pagedown
74714f942a Implement macOS accessibility protocol to provide selected text
Allow "Speak selection" (Option+Esc) to work properly.
2022-08-09 11:02:12 +08:00
pagedown
0d116e6ef0 Use four spaces to indent 2022-08-08 22:31:55 +05:30
pagedown
06108d66b1 Replace tab with four spaces indent 2022-08-08 22:31:55 +05:30
Kovid Goyal
21eece4c6d Ensure no bytecode is written when running shell based tests 2022-08-08 17:50:59 +05:30
Kovid Goyal
4dd696e4e0 Add API to glfw to get the currently active text selection 2022-08-08 15:52:34 +05:30
Kovid Goyal
d04d4444af implement primary selection api on cocoa as stubs, can be useful for #5357 2022-08-06 12:59:15 +05:30
Kovid Goyal
a487a78200 Add type definitions for the crypto data types 2022-08-05 14:39:20 +05:30
Kovid Goyal
f06a72b418 OpenSSL 3.0 apparently has API to get rid of the tag length magic number. Lets hope it works 2022-08-05 14:28:56 +05:30
Kovid Goyal
3270c9a527 Get rid of various magic length constants 2022-08-05 14:17:42 +05:30
Kovid Goyal
a7b7fb560a Add tests for AES256GCM 2022-08-05 13:50:03 +05:30
Kovid Goyal
384eba6cd4 Wrap the OpenSSL API for AES 256 GCM encryption 2022-08-05 13:14:25 +05:30
Kovid Goyal
dc7e3ba1ec DRYer 2022-08-05 08:57:22 +05:30
Kovid Goyal
b1e0adcfdc ... 2022-08-05 08:14:29 +05:30
Kovid Goyal
2a5922ce92 Cleanup previous PR
The reason maxsplit is 1 is because some layout action in the future may need more
sophisticated args processing, for example, shlex.split() instead of
plain split(), or even a full command line parser.
2022-08-05 08:12:05 +05:30
Kovid Goyal
93694925ce Merge branch 'feature/toggle_bias' of https://github.com/mong8se/kitty 2022-08-05 08:11:35 +05:30
Steven Moazami
adfcbb004f Add layout_action to change bias on tall layout
Can specify 1 bias to toggle between that and 50%.

Can specify multiple biases to rotate through them with each call to the
layout_action.

Left a comment regarding working around either a possible bug or a
design decision.
2022-08-04 19:29:17 -04:00
Kovid Goyal
6595898b73 Merge branch 'patch-4' of https://github.com/ctrlcctrlv/kitty 2022-08-04 20:45:29 +05:30
Fredrick Brennan
81b33f1f6f Document how to build docs 2022-08-04 11:12:04 -04:00
Kovid Goyal
a1c4ffec20 Private raw key should be returned as a secret 2022-08-04 20:27:38 +05:30
Kovid Goyal
89854cca8b Test for secret derivation 2022-08-04 20:05:19 +05:30
Kovid Goyal
4fe5211ed7 Code to derive the shared secret with elliptic curve keys 2022-08-04 19:42:38 +05:30
Kovid Goyal
795b32efd7 Bash integration: Fix the inherit_errexit option being set by shell integration
Fixes #5349
2022-08-04 16:23:52 +05:30
Kovid Goyal
153064ef3a Fail if mlock fails 2022-08-04 11:16:36 +05:30
Kovid Goyal
183a31c884 mlock() the returned private key to prevent it from being saved to swap 2022-08-04 11:08:54 +05:30
Kovid Goyal
adf47bed89 Use an actual type for the EC key rather than a capsule 2022-08-04 11:01:14 +05:30
Kovid Goyal
cc297a8984 Merge branch 'patch-1' of https://github.com/TomFaulkner/kitty 2022-08-04 07:39:38 +05:30
Tom Faulkner
4ef17edace Fix doc typo: throught to throughout
Just a typo in the docs. Kitty is great, thanks for all the hard work.
2022-08-03 23:52:00 +00:00
Kovid Goyal
e56fa9d31f Try and get Homebrew installed OpenSSL to work 2022-08-03 23:17:47 +05:30
Kovid Goyal
791d4b30fb We need openssl installed via brew 2022-08-03 22:47:11 +05:30
Kovid Goyal
a9a9f1ac00 Add a mappable action to toggle the mirrored setting for the tall and fat layouts
Fixes #5344
2022-08-03 22:45:38 +05:30
Kovid Goyal
fd6bc55db6 Start work on implementing public key crypto
Will come in handy for various things in the future, so lets just setup
the API now. No new dependencies are needed since Python already depends
on OpenSSL.
2022-08-03 22:37:35 +05:30
Kovid Goyal
97d7329d14 A nicer fix for kitty @ ls | less
We rely on the fact that cfmakeraw() and less's code to put the tty in
raw state diverge. This is obv a hack but then so is kitty @ ls | less

This hack allows

kitty +kitten clipboard --get-clipboard > output

to work.
2022-08-02 18:33:53 +05:30
Kovid Goyal
609d42e2bc Ignore EAGAIN when doing tty io 2022-08-02 17:21:28 +05:30
Kovid Goyal
3463931dad Rewrite clipboard kitten to only change termios settings while it is actually reading from terminal 2022-08-02 17:14:27 +05:30
Kovid Goyal
f637bf2377 socket prewarm: Dont control the tty if STDOUT is not a tty
This is because we could be being piped to a program like less that does
want to control the tty itself
2022-08-02 14:09:34 +05:30
Kovid Goyal
2271eb5e60 Document KITTY_PREWARM_SOCKET_REAL_TTY 2022-08-02 08:38:07 +05:30
Kovid Goyal
a5cbb552f0 Allow prewarming of kitty @ commands 2022-08-01 19:47:28 +05:30
Kovid Goyal
e43ca5b031 Add an implementation using splice() for in-kernel tty copies
Sadly splice is currently not functional in Linux, so the implementation
is turned off
2022-08-01 19:16:51 +05:30
Kovid Goyal
cc7e668ad6 Cleanup editing of proc environ 2022-08-01 19:03:03 +05:30
Kovid Goyal
a01dbbdf04 Fix launcher not building universal 2022-08-01 19:03:03 +05:30
Kovid Goyal
e0ce0a67cc ensure setting KITTY_PREWARM_SOCKET_REAL_TTY does not clobber the environ 2022-08-01 19:03:03 +05:30
Kovid Goyal
0d83a8866b Make setting the real tty name more robust 2022-08-01 19:03:03 +05:30
Kovid Goyal
f7a7e39a36 prewarm wrapper should indicate it is active via the process environment 2022-08-01 19:03:03 +05:30
Kovid Goyal
67cb466ea9 ... 2022-08-01 19:03:03 +05:30
Kovid Goyal
376688ab9f Add a test for SIGTSTP via ctrl-z 2022-08-01 19:03:03 +05:30
Kovid Goyal
8668087d15 flush data when exiting on signal as well
should make the signal tests less flaky
2022-08-01 19:03:03 +05:30
Kovid Goyal
9b1c23987c Fix stdout line_buffering incorrect when parent kitty instance has its stdout redirected to a file 2022-08-01 19:03:03 +05:30
Kovid Goyal
784daa1a4b Update changelog for socket prewarm 2022-08-01 19:03:03 +05:30
Kovid Goyal
06f9b73a7b Workaround mypy bug 2022-08-01 19:03:03 +05:30
Kovid Goyal
62c0272df2 Get test.py working when prewarm socket is in operation 2022-08-01 19:03:03 +05:30
Kovid Goyal
7ce2b4c641 Indicate in kitty_run_data we are prewarmed 2022-08-01 19:03:03 +05:30
Kovid Goyal
08c2de541f Nicer establish_controlling_tty 2022-08-01 19:03:03 +05:30
Kovid Goyal
94551623ec Make JSON reading test a bit more robust 2022-08-01 19:03:02 +05:30
Kovid Goyal
cb0d23bae8 Use sub tests for the various prewarm things 2022-08-01 19:03:02 +05:30
Kovid Goyal
cb5157f2d1 Nicer implementation of eintr_retry that does not prevent type checking of wrapped functions arguments 2022-08-01 19:03:02 +05:30
Kovid Goyal
32dcbbb5e1 ... 2022-08-01 19:03:02 +05:30
Kovid Goyal
4eedf0d36c macOS needs SIGTTOU SIGTTIN explicitly ignored to allow the supervisor to use TIOCSWINSZ 2022-08-01 19:03:02 +05:30
Kovid Goyal
be458303c5 Debounce WINSZ events 2022-08-01 19:03:02 +05:30
Kovid Goyal
78542ede14 Separate test for SIGWINCH handling 2022-08-01 19:03:02 +05:30
Kovid Goyal
df8800b7ff tcsetpgrp can raise EINTR despite python claiming to auto retry 2022-08-01 19:03:02 +05:30
Kovid Goyal
9c30cd8891 Use a process supervisor for socket workers
This simplifies the code and also allows SIGTSTP to work as the worker
process is no longer in an orphaned process group.
2022-08-01 19:03:02 +05:30
Kovid Goyal
dda28efd66 Use the same signal to stop as stopped the child 2022-08-01 19:03:02 +05:30
Kovid Goyal
b78183972c Report child stop to wrapper process 2022-08-01 19:03:02 +05:30
Kovid Goyal
fc06196a3e Restore termios state when stopping 2022-08-01 19:03:02 +05:30
Kovid Goyal
2a1809a2d8 When exiting with a signal run cleanup 2022-08-01 19:03:02 +05:30
Kovid Goyal
ac5c312267 Work on handling child stop events 2022-08-01 19:03:02 +05:30
Kovid Goyal
8f3174038b Fix running of test suite inside kitty 2022-08-01 19:03:02 +05:30
Kovid Goyal
2d8113e0e0 Give up on signal delivery tests 2022-08-01 19:03:02 +05:30
Kovid Goyal
e881850bb4 ... 2022-08-01 19:03:02 +05:30
Kovid Goyal
4cbae1db89 ... 2022-08-01 19:03:02 +05:30
Kovid Goyal
97bce7be18 and again 2022-08-01 19:03:01 +05:30
Kovid Goyal
5d2a276172 ... 2022-08-01 19:03:01 +05:30
Kovid Goyal
762dfe8315 increase timeout since signal delivery on CI is flakey 2022-08-01 19:03:01 +05:30
Kovid Goyal
1c0d1ec8a9 wait for child events explicitly 2022-08-01 19:03:01 +05:30
Kovid Goyal
e650699217 py3.8 compat 2022-08-01 19:03:01 +05:30
Kovid Goyal
5ff2b5be1c Clear out any signals before ending test 2022-08-01 19:03:01 +05:30
Kovid Goyal
f1841d7c81 DRYer 2022-08-01 19:03:01 +05:30
Kovid Goyal
fd6231a778 Fix signal reading in prewarm wrapper 2022-08-01 19:03:01 +05:30
Kovid Goyal
751a52153d Restore python signal handlers properly after the test 2022-08-01 19:03:01 +05:30
Kovid Goyal
61b733bae9 Add a test for SIGCHLD on SIGTSTP 2022-08-01 19:03:01 +05:30
Kovid Goyal
624e96df9b wrapper process should exit with signal if prewarmed worker exits with signal 2022-08-01 19:03:01 +05:30
Kovid Goyal
a3f1a44d83 OK I give up I cant get signal delivery to work reliably 2022-08-01 19:03:01 +05:30
Kovid Goyal
ed1547938e When running tests ensure the signal mask is empty for processes run in the testing pty 2022-08-01 19:03:01 +05:30
Kovid Goyal
f96a13e9e4 mask handled signals when running signal handler 2022-08-01 19:03:01 +05:30
Kovid Goyal
7b70ed96b6 DRYer 2022-08-01 19:03:01 +05:30
Kovid Goyal
9379853d47 Get the SIGINT test working on macOS 2022-08-01 19:03:01 +05:30
Kovid Goyal
6ccfebd9e3 ... 2022-08-01 19:03:01 +05:30
Kovid Goyal
45186a17ca macOS doesnt allow TIOCSWINSZ from any process other than the process connected to the terminal or its parent
So send it over the socket to the zygote and have it resize the
terminal. Sigh.
2022-08-01 19:03:00 +05:30
Kovid Goyal
24bb4585af Allow easily skipping tests when updating on ox 2022-08-01 19:03:00 +05:30
Kovid Goyal
feb96f1be8 Use O_CLOEXEC for temp ctty fds 2022-08-01 19:03:00 +05:30
Kovid Goyal
b7d3d64975 Ignore error condition on master fd of testing pty 2022-08-01 19:03:00 +05:30
Kovid Goyal
6114b49630 Ensure pty is in utf8 2022-08-01 19:03:00 +05:30
Kovid Goyal
87d95caae0 Dont bother sending slave fd over the socket as it doesnt help on macOS anyway 2022-08-01 19:03:00 +05:30
Kovid Goyal
9535bc94ff Use both open and the ioctl to set the controlling terminal 2022-08-01 19:03:00 +05:30
Kovid Goyal
0841be7803 Test sigwinch handling 2022-08-01 19:03:00 +05:30
Kovid Goyal
6cec89181b dont abort when failing to write to a tty 2022-08-01 19:03:00 +05:30
Kovid Goyal
cb78e17594 dont break when socket is pre-closed 2022-08-01 19:03:00 +05:30
Kovid Goyal
285c399ae5 ... 2022-08-01 19:03:00 +05:30
Kovid Goyal
c9b14f6f78 Switch to using select() instead of poll() in prewarm wrapper
This is because poll() is broken on macOS with pty fds
2022-08-01 19:03:00 +05:30
Kovid Goyal
6a101c51d2 Close tty_fd in master on fork() rather than on close() 2022-08-01 19:03:00 +05:30
Kovid Goyal
8998970adc Make the socket prewarm test a bit more robust 2022-08-01 19:03:00 +05:30
Kovid Goyal
991fbacb99 Fix ttyfd passing 2022-08-01 19:03:00 +05:30
Kovid Goyal
72f3e8cd40 Avoid passing around pty paths, instead send the pty fd 2022-08-01 19:03:00 +05:30
Kovid Goyal
cf8113ea24 Even more flexible management of poll data 2022-08-01 19:03:00 +05:30
Kovid Goyal
5d1cf49fb6 Only set control msg metadata when actually sending a control msg 2022-08-01 19:03:00 +05:30
Kovid Goyal
90f3c0f9af Workaround macOS weirdness 2022-08-01 19:02:59 +05:30
Kovid Goyal
d02598688a Show full traceback for failures to fork socket child 2022-08-01 19:02:59 +05:30
Kovid Goyal
1991f1ccd7 Better handling of multiple optional to poll fds 2022-08-01 19:02:59 +05:30
Kovid Goyal
8fad8972d5 ... 2022-08-01 19:02:59 +05:30
Kovid Goyal
fc3e78e226 Also handle SIGQUIT 2022-08-01 19:02:59 +05:30
Kovid Goyal
15aace29a1 Restore python signal handlers in forked workers 2022-08-01 19:02:59 +05:30
Kovid Goyal
12a3a2d7fe Only set switch interval after prewarm fork 2022-08-01 19:02:59 +05:30
Kovid Goyal
7e26a340a3 Try to read from child pty even after zygote signals its death 2022-08-01 19:02:59 +05:30
Kovid Goyal
f0764c24d2 Retry read/write while there is space 2022-08-01 19:02:59 +05:30
Kovid Goyal
d080bf3b9c Forward termination signals 2022-08-01 19:02:59 +05:30
Kovid Goyal
51698dff07 ... 2022-08-01 19:02:59 +05:30
Kovid Goyal
4154926be7 Get the test to pass on macOS 2022-08-01 19:02:59 +05:30
Kovid Goyal
806ca2b224 Fix atexit handlers being called in forked children
In particular the atexit handler for random_unix_socket()
2022-08-01 19:02:59 +05:30
Kovid Goyal
92bbf08343 Fix socket connect call on non-linux 2022-08-01 19:02:59 +05:30
Kovid Goyal
dc84e3de67 Do not hang forever waiting for child death 2022-08-01 19:02:59 +05:30
Kovid Goyal
8011801549 re-init openssl PRNG when forking for prewarm 2022-08-01 19:02:59 +05:30
Kovid Goyal
dfec88ed15 Inherit full outside env 2022-08-01 19:02:59 +05:30
Kovid Goyal
5d3e2c7111 ... 2022-08-01 19:02:58 +05:30
Kovid Goyal
af1c48e0f7 Dont rely on having source of test modules available 2022-08-01 19:02:58 +05:30
Kovid Goyal
d4bc906c5c Fix launcher build with sanitize 2022-08-01 19:02:58 +05:30
Kovid Goyal
62fe467336 ... 2022-08-01 19:02:58 +05:30
Kovid Goyal
ef3e9d1c2e make various compilers happy 2022-08-01 19:02:58 +05:30
Kovid Goyal
09ddbbf600 Finish move of prewarm launcher into its own compilation unit 2022-08-01 19:02:58 +05:30
Kovid Goyal
4195c239c8 Start moving prewarm launcher into its own compilation unit 2022-08-01 19:02:58 +05:30
Kovid Goyal
a6c3c57d40 Test stdio redirection with socket prewarm 2022-08-01 19:02:58 +05:30
Kovid Goyal
de9263a117 Verify uid/gid of connection from a prewarm client 2022-08-01 19:02:58 +05:30
Kovid Goyal
7b7f1ecc54 Add basic tests for socket prewarm 2022-08-01 19:02:58 +05:30
Kovid Goyal
d1b028c27a Dont pass inherited KITTY_PREWARM_SOCKET 2022-08-01 19:02:58 +05:30
Kovid Goyal
055aae2382 Use correct exit codes for signals 2022-08-01 19:02:58 +05:30
Kovid Goyal
dbb084da7a Cleanup closing of socket child 2022-08-01 19:02:58 +05:30
Kovid Goyal
8332cd2f79 Avoid needing to call os.getpid() repeatedly 2022-08-01 19:02:58 +05:30
Kovid Goyal
4e29c0c16b Propagate SIGWINCH 2022-08-01 19:02:58 +05:30
Kovid Goyal
320d45a3f5 Only use prewarm socket if uid and gid match 2022-08-01 19:02:58 +05:30
Kovid Goyal
73795b5257 detect when socket child closes its tty 2022-08-01 19:02:58 +05:30
Kovid Goyal
16e59784c6 handle failures to send data over socket gracefully 2022-08-01 19:02:58 +05:30
Kovid Goyal
65a7ec3f85 move socket launch handling into its own function 2022-08-01 19:02:57 +05:30
Kovid Goyal
360ff35061 Better error logging from prewarm zygote 2022-08-01 19:02:57 +05:30
Kovid Goyal
5098792723 Make the tty pipes non-blocking 2022-08-01 19:02:57 +05:30
Kovid Goyal
a4883e6d41 Transfer data between ttys 2022-08-01 19:02:57 +05:30
Kovid Goyal
44c9d66dd4 Use a map for pid -> socketchild 2022-08-01 19:02:57 +05:30
Kovid Goyal
c7718d078c Fix parsing of child pid and exit code 2022-08-01 19:02:57 +05:30
Kovid Goyal
2525994db4 ... 2022-08-01 19:02:57 +05:30
Kovid Goyal
81e6afa9b3 Cleanup fd transmit 2022-08-01 19:02:57 +05:30
Kovid Goyal
d8f5ccf9e4 Make master tty raw and print error messages with more context 2022-08-01 19:02:57 +05:30
Kovid Goyal
774489c578 ... 2022-08-01 19:02:57 +05:30
Kovid Goyal
39bb0ecfcf more work on socket based prewarm 2022-08-01 19:02:57 +05:30
Kovid Goyal
b222ab1bf6 Start work on socket based prewarm 2022-08-01 19:02:57 +05:30
Kovid Goyal
7a31c7ff50 Start work on everywhere prewarm 2022-08-01 19:02:57 +05:30
Kovid Goyal
132f87d152 show_key: use a better representation of the space key when 2022-08-01 13:25:56 +05:30
Kovid Goyal
aedda4bf9b ... 2022-07-31 10:36:03 +05:30
Kovid Goyal
40bba70bfe Wayland IME: Avoid printing errors for compositors that pointlessly acknowledge previous IME commits rather than just the current one. Apparently kwin is one such. Fixes #5339 2022-07-31 10:22:40 +05:30
Kovid Goyal
8202247b27 DRYer 2022-07-29 12:51:36 +05:30
Kovid Goyal
e2117ee8a4 ssh kitten: Allow completion of ssh options between the destination and command
This contravenes the documented command line parsing behavior of ssh but
matches its actual implementation. Sigh.

Fixes #5322
2022-07-29 08:29:49 +05:30
Kovid Goyal
e3a4150fea Second draw attempt has single fade edge 2022-07-29 08:15:38 +05:30
Kovid Goyal
0ce2a23af3 Allow tab title templates to use the current max title length 2022-07-29 07:43:55 +05:30
Kovid Goyal
6b2337c9c0 Merge branch 'master' of https://github.com/newbee1905/kitty-1 2022-07-26 20:27:59 +05:30
newbee1905
54661b7fc4 reflector: replace unecessary while loop 2022-07-26 21:47:52 +07:00
Kovid Goyal
a1f895b781 Merge branch 'master' of https://github.com/nullchilly/kitty 2022-07-26 19:10:34 +05:30
nullchilly
199c271edc Fix modify_font underline_position 1 2022-07-26 20:08:04 +07:00
Kovid Goyal
9ac744ae63 DRYer 2022-07-26 16:10:43 +05:30
Kovid Goyal
93b05bcd1e Make the calculation of the bound for underline_position more precise
Fixes #5308
2022-07-26 16:07:01 +05:30
Kovid Goyal
546cdbefae Don't expose all Tab methods in title template
They can have side effects so prevent user from foot shot
2022-07-26 13:13:00 +05:30
Kovid Goyal
352c07987b Nicer fix for showing cwd in tab titles 2022-07-26 12:35:07 +05:30
Kovid Goyal
f6a1eb19d7 Only import ReferenceType when type checking 2022-07-26 11:06:19 +05:30
Kovid Goyal
a352804c14 Merge branch 'misc' of https://github.com/page-down/kitty 2022-07-26 11:04:01 +05:30
pagedown
28ae8c4d20 Expand ~ when resolving editor path and applying exe_search_path 2022-07-26 13:21:50 +08:00
pagedown
1eef584382 Replace tr with string built-in in fish integration 2022-07-26 13:21:36 +08:00
pagedown
4e1cd0590b Use builtin to call read in zsh integration and bash integration 2022-07-26 13:20:50 +08:00
pagedown
fa670e1f5d ... 2022-07-26 13:20:41 +08:00
Kovid Goyal
c7fa6f620a py3.8 compat 2022-07-26 08:39:33 +05:30
Kovid Goyal
04690c8c7c Dont incur the cost of checking the cwd of a process on every tabbar update unless actually needed 2022-07-26 08:19:14 +05:30
Kovid Goyal
4345ea6602 Remove debugging code I left in the last commit accidentally 2022-07-26 08:17:44 +05:30
Kovid Goyal
d39c212336 Merge branch 'add-cwd-TabBarData' of https://github.com/nxvxl/kitty 2022-07-26 08:08:14 +05:30
Kovid Goyal
54d2f06abe remote files kitten: Fix working with files whose names have characters that need to be quoted in shell scripts
Fixes #5313
2022-07-26 08:06:25 +05:30
noval
237bfc9a6e add cwd to TabBarData
this option add current working directory to the tab by using active windows child directory.
2022-07-26 09:33:55 +07:00
Kovid Goyal
29b3d49cd5 Note that the crossterm library has added support for the kitty keyboard protocol 2022-07-26 07:45:57 +05:30
Kovid Goyal
fe8892ec54 Update changelog for previous PR 2022-07-21 20:03:46 +05:30
Kovid Goyal
2d8dd7a53e Merge branch 'rc-scroll-window' of https://github.com/page-down/kitty 2022-07-21 20:03:05 +05:30
pagedown
b493599690 Remote control: Allow half-scrolling when scrolling window by page 2022-07-21 21:05:56 +08:00
Kovid Goyal
d9371dc265 ssh kitten: Better handling of exit on keyboard interrupt 2022-07-20 21:40:51 +05:30
Kovid Goyal
ef2a12fbdf Ignore sigint while draining the tty 2022-07-20 19:40:23 +05:30
Kovid Goyal
7215c6d6be ssh kitten: Allow pressing Ctrl-C to abort ssh before the connection is completed
Fixing this involved adding a new mode to kitty where it handles
ctrl-c/z/q by sending signals to the tty foreground process group
instead of delegating to the kernel to do that. Since the pipe may be
full we have no way of knowing when the kernel will get around to
reading the signal byte. So send the signal ourselves.

Fixes #5271
2022-07-20 19:11:14 +05:30
Kovid Goyal
bd9e1f58fe ssh kitten: Make discarding of garbage data after ssh fails more robust
We now roundtrip an UUID via the terminal and only exit after receiving
it back. This guarantees that any pending data in the pipe is discarded.
2022-07-20 13:28:51 +05:30
Kovid Goyal
79dec269e1 ... 2022-07-16 10:25:18 +05:30
Kovid Goyal
d6ffbf4f3c Rebuild before and after publishing if a branch change was needed
Sometimes branches break things
2022-07-16 10:18:05 +05:30
Kovid Goyal
32588939ae Deprecate the adjust_baseline adjust_line_height and adjust_column_width options in favor of modify_font
Unifies handling and allow using pt units for those adjustments.
Note that the behavior of percentage sizes for adjust baseline is
backwards incompatible. It now uses the baseline value as the base
rather than the cell height.
2022-07-15 15:29:42 +05:30
Kovid Goyal
a02e07bfe8 Add modify_fonts to the changelog
Fixes #5265
2022-07-15 12:29:08 +05:30
Kovid Goyal
03df0c3cca Actually implement modify_font 2022-07-15 12:27:42 +05:30
Kovid Goyal
1aa50b73a1 Allow pixel based adjustments as well 2022-07-15 11:45:43 +05:30
Kovid Goyal
c910f483bf Improve formatting of config diff for modify_font 2022-07-15 11:15:57 +05:30
Kovid Goyal
14f4a8d28e Make modify_font available to C code 2022-07-15 11:06:24 +05:30
Kovid Goyal
ee931a17b0 Start work on option to modify font characteristics
Implement parsing of the option
2022-07-15 08:54:53 +05:30
Kovid Goyal
0a2ba3fb66 Merge branch 'fix-macos-launch' of https://github.com/page-down/kitty 2022-07-14 16:45:08 +05:30
pagedown
512db660a1 macOS: Fix unable to open new tab/window when there is no OS window 2022-07-14 18:25:00 +08:00
Kovid Goyal
b0666c9180 test.py now needs kitty built 2022-07-13 20:00:54 +05:30
Kovid Goyal
57d3d09679 DRYer 2022-07-13 19:49:40 +05:30
Kovid Goyal
3a17f54bca Add a note about how to exclude directories in the copy command 2022-07-12 16:23:48 +05:30
Kovid Goyal
4c2800b294 mask handled signals when running the signal handler 2022-07-10 12:52:51 +05:30
Kovid Goyal
8358377ec3 Fix reading multiple signals from buffer on macOS 2022-07-10 12:07:54 +05:30
Kovid Goyal
3ab12c616c Merge branch 'patch-1' of https://github.com/kidonng/kitty 2022-07-09 14:45:48 +05:30
Kid
ef4e784121 Fix link formatting in docs 2022-07-09 16:00:51 +08:00
Kovid Goyal
1ead20a5b9 Add a GitHub icon to the docs footer 2022-07-08 20:36:32 +05:30
Kovid Goyal
31dfee8a95 Nicer error message for zsh versions before 5.1 2022-07-08 20:28:42 +05:30
Kovid Goyal
d6492264c7 Bash integration: Fix declare not creating global variables in .bashrc
Fixes #5254
2022-07-08 18:55:04 +05:30
Kovid Goyal
7281b9dbb1 Ensure local kitty is used when running the tests 2022-07-05 21:09:09 +05:30
Kovid Goyal
4b63ee036e Run the test suite using the kitty binary 2022-07-05 21:05:03 +05:30
Kovid Goyal
fb8eb69c20 ... 2022-07-03 20:51:59 +05:30
Kovid Goyal
4b1fa2609d Match behavior of who when getting num_users by ignoring zombie pids 2022-07-03 14:46:54 +05:30
Kovid Goyal
a8b756f040 ssh kitten: A new option :code:--symlink-strategy to control how symlinks are copied to the remote machine
Fixes #5249
2022-07-03 14:03:56 +05:30
Kovid Goyal
a94a0f3026 Fix fd leak in prewarm zygote 2022-07-03 13:32:09 +05:30
Kovid Goyal
71c902eaf8 Build the launcher with debug symbols when using --debug 2022-07-03 13:28:57 +05:30
Kovid Goyal
6e7af8ce2e Merge branch 'fix-last-cmd-output' of https://github.com/page-down/kitty 2022-07-02 10:18:16 +05:30
pagedown
6c80cd040c Fix missing last line when getting output of the running command 2022-07-02 12:33:58 +08:00
Kovid Goyal
4ed413eaa7 Switch to GA2 2022-06-30 17:00:14 +05:30
Kovid Goyal
86357d2c38 ... 2022-06-29 18:53:11 +05:30
Kovid Goyal
c0d4d01b88 ... 2022-06-29 18:41:39 +05:30
Kovid Goyal
b3fcb53625 ssh kitten: Allow ssh kitten to work from inside tmux, provided the tmux session inherits the correct KITTY env vars
Fixes #5227
2022-06-29 18:40:22 +05:30
Kovid Goyal
4f29dea8ef Utility function to detect when running inside tmux 2022-06-29 17:51:38 +05:30
Kovid Goyal
4f8ef05058 Linux: Update cursor position after all key presses not just pre-edit text changes
Fixes #5241
2022-06-29 11:37:35 +05:30
Kovid Goyal
7fc1735a21 swaps_disallowed is used only on wayland 2022-06-28 20:19:52 +05:30
Kovid Goyal
1e49038747 Fix edit-in-kitty not working with editors that use file replace to save changes 2022-06-28 11:51:03 +05:30
Kovid Goyal
295fbe42ad Merge branch 'misc' of https://github.com/page-down/kitty 2022-06-28 11:42:37 +05:30
Kovid Goyal
60d33f2847 Wayland: Reduce flicker at startup by not using render frames immediately after a resize
Fixes #5235
2022-06-28 11:29:57 +05:30
Kovid Goyal
cd369f633a Wayland: Ensure that opengl buffer swapping does not happen till the xdg surface is configured 2022-06-28 10:47:33 +05:30
pagedown
c3df6d2fe3 Remove executable perms from kitten files 2022-06-28 11:59:47 +08:00
Kovid Goyal
72dfa74b3e Avoid an extra buffer swap during startup
glfw should not be swapping buffers behind the clients back. Since we
control the client, we can ensure that the OpenGL VRAM is cleared before
the window is shown.

Also fixes the issue with the glfw wayland backend where the buffer was
being created before the surface was configured
2022-06-28 09:25:24 +05:30
pagedown
1411269187 Docs: Improve documentation for remote commands
Use text roles in the document to apply the style in the cli output.
Add more links.
Unify the text roles.
Use code blocks instead of quote blocks.
Use '+' to connect the shortcut key names.
2022-06-28 11:52:20 +08:00
pagedown
185d959f25 ssh kitten: Add MacPorts paths to the common locations for login shell 2022-06-28 11:43:19 +08:00
pagedown
1652d41992 Fix edit-in-kitty not working when option editor comes with arguments 2022-06-28 11:41:27 +08:00
pagedown
297dea881d Use the same fish completion function for commands 2022-06-28 11:35:11 +08:00
pagedown
01df828353 Refactor the fish implementation of edit-in-kitty
Clean up global variable and signal handling function before exit.
For commands allowing the use of valid functions or binary executables.
2022-06-28 11:31:42 +08:00
pagedown
617115d447 Docs: edit-in-kitty works with neovim 2022-06-28 11:22:52 +08:00
Kovid Goyal
31f9978599 Merge branch 'patch-1' of https://github.com/AnonymouX47/kitty 2022-06-27 15:54:23 +05:30
My Avatar ;D
dc068c16e8 Update projects using kitty graphics protocol
Add `term-image` to the list of projects using the kitty graphics protocol.
2022-06-27 10:35:39 +01:00
Kovid Goyal
89626a462f ... 2022-06-25 20:46:02 +05:30
Kovid Goyal
a043588b4a Update changelog for last PR 2022-06-25 16:50:46 +05:30
Kovid Goyal
2e08ee16c2 Merge branch 'fix-macos-jis-kbd' of https://github.com/page-down/kitty
Fixes #5232
2022-06-25 16:49:46 +05:30
pagedown
2cc506476b macOS: Fix JIS Eisu and Kana keys not working 2022-06-25 19:01:02 +08:00
Kovid Goyal
0a462ad706 Forgot one place to change over to kitty_run_data 2022-06-24 10:31:55 +05:30
Kovid Goyal
81c1bfe1c5 ... 2022-06-24 09:26:38 +05:30
Kovid Goyal
8ce80e39d9 kitty now needs python >= 3.8 2022-06-24 00:01:35 +05:30
Kovid Goyal
fa823a37ae Backport 6281f498c8 2022-06-23 13:00:28 +05:30
Kovid Goyal
17f67fbe28 Add a note that WezTerm implements the kitty keyboard protocol as well 2022-06-23 11:22:01 +05:30
Kovid Goyal
560b78bc35 Dont leak kitty_run_data on error 2022-06-23 08:59:31 +05:30
Kovid Goyal
f023f047ff Workaround for Python 3.11 breaking sys._xoptions
Apparently in Python-land its acceptable behavior to break backward
compatibility with documented interfaces on a whim. Bloody joke.
https://github.com/python/cpython/pull/28823

Fixes #5223
2022-06-23 08:44:34 +05:30
Kovid Goyal
5673359be2 Merge branch 'sam/modern-whiskers-icon' of https://github.com/samholmes/kitty 2022-06-22 04:11:41 +05:30
Samuel Holmes
666696d142 Add "modern whiskers" alternative icon to FAQ 2022-06-21 11:04:24 -07:00
Kovid Goyal
f95bc48f9b kitty @ set-colors: Fix changing inactive_tab_foreground not working
Fixes #5214
2022-06-19 20:20:58 +05:30
Kovid Goyal
8d18fc09fa Merge branch 'fix-rc-new-window' of https://github.com/page-down/kitty 2022-06-19 18:21:22 +05:30
Kovid Goyal
a3d42a90f2 Get rid of os.waitid() since its not present on macOS we cannot rely on it anyway 2022-06-19 18:11:23 +05:30
pagedown
1da5f2ecc6 Fix the rc command new-window does not open the shell by default 2022-06-19 20:25:12 +08:00
Kovid Goyal
d226e82f68 It is only possible to not have an OS Window on macOS 2022-06-19 09:39:40 +05:30
Kovid Goyal
60cc2cb26c Merge branch 'fix-rc-launch' of https://github.com/page-down/kitty 2022-06-19 09:38:30 +05:30
pagedown
1ac90cf464 Allow remote control to launch a new OS window when there is none 2022-06-19 11:51:58 +08:00
Kovid Goyal
d3ffaebd17 Ignore exceptions in kitten result processors 2022-06-18 08:20:01 +05:30
Kovid Goyal
bf01c549f5 Allow kittens to handle multiple results in a window 2022-06-18 08:18:25 +05:30
Kovid Goyal
6912d8c086 No need for entire screen object when scaling a scroll 2022-06-18 06:55:28 +05:30
Kovid Goyal
a89e1b5573 Fix horizontal wheel events not being reported to client programs when they grab the mouse
Fixes #2819
2022-06-18 06:47:42 +05:30
Kovid Goyal
3a9c6088b2 Add debug output for scroll events 2022-06-18 05:54:26 +05:30
Kovid Goyal
fba629f385 Merge branch 'globinclude-sorted' of https://github.com/amarshall/kitty 2022-06-18 04:59:25 +05:30
Andrew Marshall
90477825c2 Make globinclude sort files before loading
`Path.glob` gives no guarantee about the order results are returned in.
This means that if files included by `globinclude` override each other,
the resulting config was non-deterministic. Resolve this by sorting the
results from `glob` to give consistent, well-defined order.
2022-06-17 18:50:16 -04:00
Kovid Goyal
1e56d39777 Wakeup main loop when refreshing window as well as io loop 2022-06-17 19:33:43 +05:30
Kovid Goyal
2829971412 Merge branch 'expose-wakeup-main-loop' of https://github.com/sirno/kitty 2022-06-17 19:09:07 +05:30
Nicolas Ochsner
c147fda6ca Expose wakeup main loop in python api. 2022-06-17 15:26:16 +02:00
Kovid Goyal
a27dfd8b7c Increase the timeout for prewarm I/O on CI 2022-06-14 21:17:28 +05:30
Kovid Goyal
a7c65efc45 Ensure local kitty is used in tests even when another kitty is present in the system PATH
Fixes #5194
2022-06-14 21:08:58 +05:30
Kovid Goyal
646a524c8e Sort imports 2022-06-14 20:06:12 +05:30
Kovid Goyal
f8d01d2c4f Use the same parser for resize_window actions and session setting 2022-06-14 20:03:21 +05:30
Kovid Goyal
1d846f3744 Cleanup previous PR
Merge branch 'session-resize-window' of https://github.com/sirno/kitty
2022-06-14 19:59:01 +05:30
Nicolas Ochsner
6bbe58f876 Change to pass window specs. 2022-06-14 12:28:41 +02:00
Nicolas Ochsner
36719fb086 Cleanup. 2022-06-14 11:23:35 +02:00
Nicolas Ochsner
78fd7a8458 Enable resizing in session files. 2022-06-14 11:09:26 +02:00
Kovid Goyal
f5976d0d63 Remove unneeded code 2022-06-13 21:33:07 +05:30
Kovid Goyal
b313fc3fbe Forgot to also add SIGUSR1 handler 2022-06-13 21:09:32 +05:30
Kovid Goyal
cf667b8c47 Use a signal handler instead of a pipe for child death notification 2022-06-13 20:40:04 +05:30
Kovid Goyal
7e3bd8586f Dont use SIGTSTP and SIGCONT in the test as they are very flaky 2022-06-13 20:29:58 +05:30
Kovid Goyal
d5df301317 Finer grained polling 2022-06-13 19:41:40 +05:30
Kovid Goyal
628246c3da Workaround for python 3.8 that doesnt have the CLD_* constants 2022-06-13 19:22:37 +05:30
Kovid Goyal
d228acd30a Use a busy loop for signaled process 2022-06-13 19:11:29 +05:30
Kovid Goyal
18cd97d914 Skip SIGSTSTP test when CLD_STOPPED is not available 2022-06-13 19:05:49 +05:30
Kovid Goyal
5f13946bac Allow using our signal handlers in python event loops via an fd
pythons signal fd only return signal numbers not the full siginfo struct
2022-06-13 18:52:23 +05:30
Kovid Goyal
8fb24fbc1e poll() takes times in ms not seconds 2022-06-13 18:51:35 +05:30
Kovid Goyal
f0d4952055 Use an iterator 2022-06-13 08:14:51 +05:30
Kovid Goyal
7113580db2 macOS has no waitid
Le Sigh
2022-06-13 02:19:51 +05:30
Kovid Goyal
163c211219 Use waitid() rather than waitpid() to get full exit status 2022-06-13 00:19:56 +05:30
Kovid Goyal
6993157b3a ... 2022-06-13 00:15:07 +05:30
Kovid Goyal
e2efdf674d Reap prewarmed processes 2022-06-13 00:11:16 +05:30
Kovid Goyal
9fcb8e5b6e Close unneeded fds in forked children 2022-06-12 20:26:20 +05:30
Kovid Goyal
a1a637c7f1 Restore SIGUSR1 handling in prewarmed child 2022-06-12 18:59:55 +05:30
Kovid Goyal
4f87f3e9f4 ... 2022-06-12 18:15:23 +05:30
Kovid Goyal
da6faa656c Use a fork() without exec() to create prewarm process
This has the advantages:

1) Even first kitten use is fast
2) Computer has to do less work overall since prewarm process is itself prewarmed
2022-06-12 18:06:51 +05:30
Kovid Goyal
e1ad75e932 Graphics protocol: Make it explicit what is supposed to happen when two images with the same z-index overlap
Fixes #5187
2022-06-10 22:10:11 +05:30
Kovid Goyal
646bdbd002 Wayland: Fix a regression in the previous release that caused mouse cursor animation and keyboard repeat to stop working when switching seats
Fixes #5188
2022-06-10 21:38:30 +05:30
Kovid Goyal
9184de1997 ... 2022-06-10 18:44:23 +05:30
Kovid Goyal
eabc29ec4d Changelog entry for prewarming 2022-06-09 10:43:00 +05:30
Kovid Goyal
e338cdab31 Increase the timeout when talking to prewarm process 2022-06-09 07:01:42 +05:30
Kovid Goyal
7001d609fa Fix spurious warning from runpy module when prewarmed 2022-06-09 07:01:42 +05:30
Kovid Goyal
c8399ce3d2 clear signal handlers in prewarm controller 2022-06-09 07:01:42 +05:30
Kovid Goyal
3e1d701dce DRYer 2022-06-09 07:01:41 +05:30
Kovid Goyal
a385c39c2e Fix reading of dead prewarmed child pids 2022-06-09 07:01:41 +05:30
Kovid Goyal
5d1cf339a7 Simplify child death tracking
Re-use the existing fd instead of catching signals
2022-06-09 07:01:41 +05:30
Kovid Goyal
fafb8518e0 Fix compilation on platforms without SO_PASSCRED 2022-06-09 07:01:41 +05:30
Kovid Goyal
6807f4d4ea Report the correct cmdline for prewarmed processes 2022-06-09 07:01:41 +05:30
Kovid Goyal
0ed7b2f3c0 Manage fd lifecycles a bit better 2022-06-09 07:01:41 +05:30
Kovid Goyal
116128ebb5 Get prewarm working
Needed to wait in the control process for child to set its controlling
terminal so that closing the slave fd in kitty is safe.
2022-06-09 07:01:41 +05:30
Kovid Goyal
0c870c5fcd handle child death and config reload for prewarmed processes 2022-06-09 07:01:41 +05:30
Kovid Goyal
90bc3ab770 Function to create a randomly named UNIX domain socket 2022-06-09 07:01:41 +05:30
Kovid Goyal
0021b0c242 Prewarm all kittens 2022-06-09 07:01:41 +05:30
Kovid Goyal
6dc076f5d1 Complete move to prewarm.py 2022-06-09 06:54:05 +05:30
Kovid Goyal
2dd1201c3f Dont use a kitten for prewarm 2022-06-09 06:53:51 +05:30
Kovid Goyal
9be68966b4 Also detect ssh kitten run as an overlay 2022-06-08 18:53:54 +05:30
Kovid Goyal
47d482dca9 Clear signal handlers when running processes 2022-06-08 17:50:42 +05:30
Kovid Goyal
18dd13c872 Make init_loop_data a little safer to use 2022-06-08 15:10:52 +05:30
Kovid Goyal
c71bb877c3 Handle SIGHUP gradefully 2022-06-08 09:06:28 +05:30
Kovid Goyal
eee8106f9e Fix a regression that broke completion when using a kitty alias instead of having kitty on PATH 2022-06-08 07:36:09 +05:30
Kovid Goyal
80ad596d3d Yet another place where macos_colorspace was broken 2022-06-08 07:17:34 +05:30
Kovid Goyal
9dbafcc368 Remove unneccessary argument to connection_sharing_args() 2022-06-07 20:40:01 +05:30
Kovid Goyal
9f5f113840 ... 2022-06-07 13:05:07 +05:30
Kovid Goyal
6afbdbe94f Use correct kitty config in prewarmed process 2022-06-07 12:53:39 +05:30
Kovid Goyal
061a0c8cb6 Reset global options object in the tests 2022-06-07 10:58:03 +05:30
Kovid Goyal
ad128a3c29 version 0.25.2 2022-06-07 07:32:35 +05:30
Kovid Goyal
b71d2e04ca Clarify behavior of placement id when image id is zero 2022-06-07 07:22:12 +05:30
Kovid Goyal
cc3c7b0286 Fix waitpid() call on python 3.8 2022-06-06 21:22:04 +05:30
Kovid Goyal
bf6c90a69a Compare realpaths for cwd test 2022-06-06 20:46:49 +05:30
Kovid Goyal
4c9a75c1ba Remove unused code 2022-06-06 20:45:40 +05:30
Kovid Goyal
98f46f8bd7 Start work on prewarming
The prewarm process and its controller are implemented with some basic
tests.
2022-06-06 20:39:36 +05:30
Kovid Goyal
dec62b1929 Allow ignoring SHM close failures
Useful if we export the mmap beyond the lifetime of the shm object
2022-06-06 20:31:13 +05:30
Kovid Goyal
79f7954048 Cleanup previous PR 2022-06-06 17:19:52 +05:30
Kovid Goyal
fc217dafba Merge branch 'master' of https://github.com/suvayu/kitty 2022-06-06 16:55:08 +05:30
Kovid Goyal
a6aff817cf Prepare for prewarm testing 2022-06-06 15:59:34 +05:30
Suvayu Ali
38cb18fe92 diff kitten: tests for directory walking 2022-06-06 11:01:45 +02:00
Suvayu Ali
20b6a97159 Update in response to feedback
- one configuration option: ignore_paths
- use shlex to parse option to support whitespace and in-line comments
- change option type to Tuple[str, ...]
- remove ignored directories from dirnames to prevent scanning
2022-06-06 11:00:45 +02:00
Kovid Goyal
98f6e24106 Move block outside try/except as it doesnt need to be in there 2022-06-06 08:17:15 +05:30
Kovid Goyal
4ed6364b61 Fix compiler warning on 32bit builds 2022-06-06 07:19:52 +05:30
Kovid Goyal
cd7751681d hints kitten: Fix surrounding quotes/brackets and embedded carriage returns not being removed when using line number processing
Fixes #5170
2022-06-05 18:07:16 +05:30
Kovid Goyal
76531d2f7a Python wrapper function to establish controlling tty 2022-06-05 15:36:05 +05:30
Suvayu Ali
eea652f1d0 kittens/diff: move empty pattern check to dir tree walk 2022-06-05 10:11:21 +02:00
Kovid Goyal
f20f4762a8 Use builtin facility for removing cloexec 2022-06-05 12:33:34 +05:30
Kovid Goyal
c004fd1f4d Declare the set of handled signals in only one place 2022-06-05 08:01:26 +05:30
Kovid Goyal
f29039db29 Make the full signal info available in our signal handler 2022-06-05 07:22:38 +05:30
Suvayu Ali
fbf1ec43c7 diff kitten: add options to ignore paths when comparing directories
Tested locally & over SSH:

    $ kitty +kitten diff /local/path /local/path2
    $ kitty +kitten diff /local/path ssh:remote:/path
2022-06-05 00:11:30 +02:00
Kovid Goyal
a42200a430 Use the log_error function to log SIGUSR2 2022-06-04 15:38:56 +05:30
Kovid Goyal
ad576c333c Also restore signal handlers for SIGUSR1 and SIGUSR2 2022-06-04 15:15:39 +05:30
Kovid Goyal
5a0a980648 Make getting cmdline of a PID a bit more convenient 2022-06-04 15:07:08 +05:30
Kovid Goyal
b3578a4fa5 ... 2022-06-04 14:54:32 +05:30
Kovid Goyal
3c952b01d3 Fix reloading in all kitties skipping kitty processes started as kitty +open 2022-06-04 14:53:13 +05:30
Kovid Goyal
b1453a51f6 Better error message for kitty + 2022-06-04 14:47:13 +05:30
Kovid Goyal
2c3a7b5f41 Restore a couple more signal handlers that python nukes 2022-06-04 14:30:08 +05:30
Kovid Goyal
0df5a6c6c8 Utility function to test if PID is alive 2022-06-04 14:14:24 +05:30
Kovid Goyal
bf4dc6365a Add support for sigqueue() and also handle SIGUSR2
I anticipate using sigqueue() for simpler handling of SIGCHLD
notifications from the prewram process to the its parent.
2022-06-04 14:09:45 +05:30
Kovid Goyal
ce3322bf91 Remote control: Fix commands with asynchronous payloads not sending responses
Fixes #5165
2022-06-04 10:13:21 +05:30
Kovid Goyal
835cb31501 Fix incorrect dispatch of poll events when more than one peer is present 2022-06-04 09:26:57 +05:30
Kovid Goyal
762dd065f3 Nicer reverse loop 2022-06-04 09:06:35 +05:30
Kovid Goyal
7b91447d89 Use a full chekcout in CI when building docs
The :commit: docs role requires it
2022-06-04 08:00:00 +05:30
Kovid Goyal
1146c4ea3f Merge branch 'ignore-fig-osc' of https://github.com/grant0417/kitty 2022-06-04 07:44:17 +05:30
grant0417
729f7b8e03 Add custom message for Fig OSC 2022-06-03 18:57:53 -07:00
grant0417
d3ec632b9f Ignore Fig OSC codes 2022-06-03 17:07:44 -07:00
Kovid Goyal
cb01b5ee44 DRYer 2022-06-03 21:38:49 +05:30
Kovid Goyal
0828096e9e ... 2022-06-03 18:30:14 +05:30
Kovid Goyal
ebcbed290f Make loading of kitty options in kitten re-useable 2022-06-03 17:49:58 +05:30
Kovid Goyal
d6afe6f2cb Move entrypoints into their own module 2022-06-03 11:12:13 +05:30
Kovid Goyal
4daf70f636 ... 2022-06-03 11:08:41 +05:30
Kovid Goyal
2f3dd72c84 Weston: Fix client side decorations flickering on slow computers during window resize
Fixes #5162
2022-06-03 11:00:56 +05:30
Kovid Goyal
dca8c65376 Fix display of url open failures not working 2022-06-02 10:41:08 +05:30
Kovid Goyal
12d8175d9a Use inode/executable rather than application/executable 2022-06-02 10:33:56 +05:30
Kovid Goyal
609bcd68dd Fix #5161 2022-06-02 10:32:43 +05:30
Kovid Goyal
313136803c DRYer 2022-06-02 10:21:34 +05:30
Kovid Goyal
b937033411 macOS: Allow opening executables without a file extension with kitty as well
Fixes #5160
2022-06-02 09:25:30 +05:30
Kovid Goyal
55eeb9c11e Dont import kitty.complete in ssh.main 2022-06-02 08:08:33 +05:30
Kovid Goyal
8fbec2dfd5 remove unused arg 2022-06-02 08:07:53 +05:30
Kovid Goyal
c4521290b8 ... 2022-06-01 19:43:41 +05:30
Kovid Goyal
e12a9f3caf Iosevka: Fix incorrect rendering when there is a combining char that does not group with its neighbors
Fixes #5153
2022-06-01 12:00:53 +05:30
Kovid Goyal
56f8a06362 DRYer 2022-06-01 08:47:19 +05:30
Kovid Goyal
5c4abe749b Merge branch 'fontconfig-special-sources' of https://github.com/leungbk/kitty 2022-06-01 08:33:13 +05:30
Brian Leung
62c27b3db7 Add kitty/fontconfig.c to SPECIAL_SOURCES
Fixup of bec620a8d3.
2022-05-31 19:58:42 -07:00
Kovid Goyal
1283a2b7e5 ... 2022-06-01 08:21:23 +05:30
Kovid Goyal
bec620a8d3 Fix #5154 2022-06-01 08:19:11 +05:30
Kovid Goyal
6b0bb2e845 Sphinx v5 needs language to be not None 2022-06-01 06:47:06 +05:30
Kovid Goyal
135818f34b Note about __main__.py with +launch 2022-05-31 07:56:01 +05:30
Kovid Goyal
2a361d1de1 Update changelog for last PR 2022-05-31 07:46:00 +05:30
Kovid Goyal
761a941d43 Merge branch 'kovidgoyal-master' of https://github.com/jimbob3806/kitty 2022-05-31 07:41:40 +05:30
Kovid Goyal
c03630a487 ... 2022-05-31 07:26:04 +05:30
James Reid
0b79cbdb3b Add "user" tab to themes kitten
Add new tab named "user" to the built-in themes kitten - new tab
filters and displays only user-defined color themes, identified by
adding "is_user_defined" property to "Theme" class which defaults
to False, and is set True when a theme is fetched using the
"load_from_dir" method.
2022-05-30 19:25:09 +01:00
James Reid
1dc6b49e02 Remove hard coded tab string in ThemesHandler
Remove hard coded tab string and replace with self.tabs, which is
a reference to the same tab string defined in the __init__ of
ThemesHandler
2022-05-30 19:17:07 +01:00
Kovid Goyal
1962f8e5a0 Advertise edit-in-kitty a bit more 2022-05-30 15:27:23 +05:30
Kovid Goyal
cabfcbcb69 macOS: When pasting text and the clipboard has a filesystem path, paste the full path instead of the text, which is sometimes just the file name
Fixes #5142
2022-05-30 14:40:45 +05:30
Kovid Goyal
48bb43a2c4 fish: completion for edit-in-kitty clone-in-kitty
fish's wrapping facility does not preserve argv[0] so it cannot be used
unlike for zsh and bash
2022-05-30 10:50:19 +05:30
Kovid Goyal
af5e3e0c18 Add a reference to edit-in-kitty to the remote file kitten docs 2022-05-30 08:19:33 +05:30
Kovid Goyal
fb8398c2eb bash: completion for edit/clone-in-kitty 2022-05-30 08:16:51 +05:30
Kovid Goyal
eefd4bb3e3 zsh: Completion for edit-in-kitty and clone-in-kitty 2022-05-30 08:12:09 +05:30
Kovid Goyal
581c388838 ... 2022-05-29 15:02:37 +05:30
Kovid Goyal
7a63ea6480 Drop python 3.7 from CI 2022-05-29 14:54:51 +05:30
Kovid Goyal
65f1329635 Wayland: When the seat is closed remove any pending pointer animation and key repeat timers
Fixes #5145
2022-05-29 14:50:34 +05:30
Kovid Goyal
0df9a5d5c5 Fix compilation on Python 3.11
Python 3.11 deprecates the supposedly "stable API" PySys_AddXOption().
So switch to using PyConfig.xoptions instead. This means dropping
support for python 3.7 since PyConfig is not available there. So it
rolls.

Fixes #5143
2022-05-29 14:37:14 +05:30
Kovid Goyal
6405849a1b Bash integration clone test was accidentally running zsh
Fixes #5144
2022-05-29 08:46:06 +05:30
Kovid Goyal
e9340dfaaf Some more tests for last non empty command output 2022-05-28 15:22:17 +05:30
Kovid Goyal
0509855930 Allow getting the last non-empty command output easily via an action or remote control
Fixes #4973
2022-05-28 15:19:24 +05:30
Kovid Goyal
4c74462763 ssh kitten: Look in common locations for login shell if it is not in PATH 2022-05-28 11:29:09 +05:30
Kovid Goyal
7f69381221 Update changelog 2022-05-28 11:13:35 +05:30
Kovid Goyal
75942fb47e Implement edit-in-kitty for fish 2022-05-28 11:12:11 +05:30
Kovid Goyal
0893d7164a _run_kitten -> run_kitten_with_metadata 2022-05-28 11:00:25 +05:30
Kovid Goyal
27906ea853 Skip login shell detection when login shell is set to nologin 2022-05-28 08:09:30 +05:30
Kovid Goyal
8540ca3990 Workaround for nix package messing with kitty file permissions 2022-05-28 08:03:17 +05:30
Kovid Goyal
e82c5a63b5 Avoid unnecessary use of cat 2022-05-27 14:18:16 +05:30
Kovid Goyal
83602d7e53 Make chunked transmit code re-useable in fish integration script 2022-05-27 13:21:26 +05:30
Kovid Goyal
64e30e5299 Ignore permission errors when stating file to edit 2022-05-27 13:04:02 +05:30
Kovid Goyal
83ce23ff9f Use zsh's builtin stat instead of relying on the stat command 2022-05-27 12:50:52 +05:30
Kovid Goyal
c5e5ec618d Also trap SIGTERM to cleanup 2022-05-27 12:39:22 +05:30
Kovid Goyal
bd84ec03ce Implement edit-in-kitty for bash 2022-05-27 12:38:26 +05:30
Kovid Goyal
d71f18566e forgot to declare data local 2022-05-27 12:29:11 +05:30
Kovid Goyal
76e75d9f89 Ensure self.tdir exists in __del__ 2022-05-27 10:48:16 +05:30
Kovid Goyal
f5695ac4f4 Increase edit file size limit to 8MB 2022-05-27 10:45:36 +05:30
Kovid Goyal
b748cd21cf Allow opening files at a spedific line number with edit-in-kitty 2022-05-27 09:53:43 +05:30
Kovid Goyal
3a8bdf3b48 React instantly to editor window being closed 2022-05-27 09:15:16 +05:30
Kovid Goyal
196c6b07aa Merge branch 'fix-window-menu' of https://github.com/henryhchchc/kitty 2022-05-27 09:12:56 +05:30
Henry Chu
1978cc5b55 Fixed #5132 2022-05-27 11:15:16 +08:00
Kovid Goyal
ea47cee23f Document edit-in-kitty 2022-05-27 07:41:54 +05:30
Kovid Goyal
e6844ad6f2 ssh kitten: Run bash as a login shell
This fell through the cracks in the rewrite for automatic shell
integration. Fixes #5130
2022-05-26 21:24:32 +05:30
Kovid Goyal
02d5b3eafa Fix shebang 2022-05-26 21:21:36 +05:30
Kovid Goyal
151dacf17f Update changelog 2022-05-26 21:21:27 +05:30
Kovid Goyal
117d1b02be Start work on edit-in-kitty 2022-05-26 21:16:42 +05:30
Kovid Goyal
cb55878efd Fix #5129 2022-05-26 19:42:20 +05:30
Kovid Goyal
ea4751eae5 Move logic for handling chunked remote data into a separate function
Use the existing clipboard_max_size as the limit for remote data
2022-05-26 08:52:20 +05:30
Kovid Goyal
cc4f1c0a24 version 0.25.1 2022-05-26 07:59:25 +05:30
Kovid Goyal
3eb5320e29 Add tpix to list of programs that speak the graphics protocol 2022-05-25 12:27:38 +05:30
Kovid Goyal
4f63cff1a4 Make _run_kitten public API 2022-05-25 07:14:18 +05:30
Kovid Goyal
8381171c8d Forgot trailing newline 2022-05-24 06:54:53 +05:30
Kovid Goyal
1b2d54d97a Output a warning when libwayland-cursor returns incorrectly sized cursor images 2022-05-23 12:50:47 +05:30
Kovid Goyal
e7da874b82 GNOME: Workaround for GNOME's text input system going into an infinite loop if cursor position is updated in response to a done event
Fixes #5105
2022-05-23 10:36:58 +05:30
Kovid Goyal
ea8bed2a71 Fix #5113 2022-05-20 20:45:08 +05:30
Kovid Goyal
c747e681a2 Merge branch 'patch-1' of https://github.com/jack1142/kitty 2022-05-20 20:42:11 +05:30
Jakub Kuczys
a9a9ec80b5 Support the short pretty flag (-p) in hyperlinked_grep kitten 2022-05-20 10:57:42 +02:00
Kovid Goyal
eb84990f5a Fix #5110 2022-05-19 09:55:12 +05:30
Kovid Goyal
1938ba3108 Add changelog entry for previous PR 2022-05-18 20:04:09 +05:30
Kovid Goyal
0eee2db199 Merge branch 'add-option-select_by_word_characters_forward' of https://github.com/kgraefe/kitty 2022-05-18 20:02:11 +05:30
Kovid Goyal
bba1455e28 Dont import all of the ssh kitten just to detect commandlines 2022-05-18 15:58:06 +05:30
Konrad Gräfe
8c41cc8d3e Add option select_by_word_characters_forward
This adds an option to select separate characters for forward and
backward word extension on double click. If it is empty the old behavior
is preserved. This is the default.

If it is not empty, select_by_word_characters_forward will be used for
extending the selection in forward direction (right) and
select_by_word_characters will be used for extending in backward
direction (left).

Signed-off-by: Konrad Gräfe <kgraefe@paktolos.net>
2022-05-16 11:09:36 +02:00
Kovid Goyal
5efcb35cfb Only integrate with ssh kitten if shared connections are enabled for that host 2022-05-14 10:37:05 +05:30
Kovid Goyal
1b4cf1fea7 Remote file kitten: Integrate with the ssh kitten 2022-05-14 10:31:18 +05:30
Kovid Goyal
d3656bf7e9 Linux: Load libfontconfig at runtime to allow the binaries to work for running kittens on servers without FontConfig 2022-05-13 19:56:19 +05:30
Kovid Goyal
aaffec1cbc Better error message 2022-05-13 19:25:37 +05:30
Kovid Goyal
ed9391d4d6 Add a note explaining why fontconfig is not bundled 2022-05-13 17:45:16 +05:30
Kovid Goyal
58333f260b Fix spurious error print in test 2022-05-13 17:42:00 +05:30
Kovid Goyal
cf8ccabad9 Fix deleting images by row not calculating image bounds correctly
Fixes #5081
2022-05-13 09:51:25 +05:30
Kovid Goyal
600c595fdf Fix reloading of config not working when kitty.conf does not exist when kitty is launched
Fixes #5071
2022-05-08 20:57:23 +05:30
Kovid Goyal
2427d2d9da Workaround Apple's "hardening" breaking the platform module
Fix #5065
Fix #5051
2022-05-07 20:43:10 +05:30
Kovid Goyal
4ca70bfa26 ... 2022-05-04 13:21:17 +05:30
Kovid Goyal
4e8eb2f7f0 Merge branch 'rc-match' of https://github.com/page-down/kitty 2022-05-01 21:23:58 +05:30
pagedown
b6cf4bc78f ... 2022-05-01 23:27:33 +08:00
pagedown
622cd5531a Raise an exception when not in broadcast and no match for send-text
send-text still won't return an error code when there is no match,
since no_response is True by default.
2022-05-01 23:05:44 +08:00
pagedown
ec35b0cc7c Fix remote command --match=all returns empty 2022-05-01 23:05:22 +08:00
pagedown
0719c7f8bb DRYer: Use window matching function in remote commands 2022-05-01 23:04:50 +08:00
Kovid Goyal
bb7e4039e8 Merge branch 'rc-err' of https://github.com/page-down/kitty 2022-05-01 07:11:21 +05:30
pagedown
a0559e506b Show error message when remote command arguments are invalid 2022-04-30 22:01:58 +08:00
Kovid Goyal
4ce4176bbe cleanup previous PR 2022-04-30 15:03:27 +05:30
Kovid Goyal
99409f0a8b Merge branch 'docs' of https://github.com/page-down/kitty 2022-04-30 14:41:12 +05:30
pagedown
13303416b8 Docs: Improve the documentations
Add more text roles and links.
Fix typos.
Adjust the default shortcut order in overview.
Notes on the impact of kitty-open.desktop on the default open apps.
Document KITTY_DEVELOP_FROM in glossary.
Add Python type hints to the watcher example.
Mention clone-in-kitty in launch --copy-env.
Fix remote control ls example does not work, by escaping the backslash.
2022-04-30 17:07:36 +08:00
Kovid Goyal
9811d677e5 query terminal kitten: Return fast when run in non-kitty terminals 2022-04-30 14:25:39 +05:30
pagedown
b9fd039668 Docs: Document the env vars set by kitty for auto shell integration 2022-04-30 16:54:53 +08:00
pagedown
6c1f20bb27 Docs: Mention close_shared_ssh_connections in ssh kitten config docs 2022-04-30 16:54:46 +08:00
pagedown
2b58316c64 ... 2022-04-30 16:54:41 +08:00
Kovid Goyal
f2df629634 ... 2022-04-30 09:38:08 +05:30
Kovid Goyal
8f46505a50 Header to print stack traces in C 2022-04-30 09:28:05 +05:30
Kovid Goyal
c48bf4fd85 Output debug lines for when kitty sets the pointer in Wayland 2022-04-30 09:13:08 +05:30
Kovid Goyal
8c67f5aea8 Merge branch 'fix-gen-conf' of https://github.com/page-down/kitty 2022-04-30 07:35:15 +05:30
pagedown
76f84e32c4 Fix the generated sample kitty.conf containing invalid options 2022-04-30 02:23:33 +08:00
Kovid Goyal
4a7125ec92 ssh kitten: Suppress error prints about invalid items in kitty.conf
Fixes #4985
2022-04-29 20:39:32 +05:30
Kovid Goyal
92c3af6a92 Allow using --match=all in all rc commands 2022-04-29 17:17:07 +05:30
Kovid Goyal
443f36ebc7 Merge branch 'docs' of https://github.com/page-down/kitty 2022-04-29 07:11:40 +05:30
pagedown
17632dea3c Docs: Improve remote control command documentation
Improve the --match option description.
Add more text roles and links.
Fix typos.
2022-04-29 01:31:11 +08:00
pagedown
bb78dc5ccb Docs: Fix the help text of query_terminal and other minor tweaks
Use --match-tab in broadcast kitten example.
2022-04-29 01:30:45 +08:00
Kovid Goyal
2737cb7dd0 Fix #5026 2022-04-28 22:12:43 +05:30
Kovid Goyal
08b2ce444f Also dont apply umask when extracting bootstrap data 2022-04-28 17:28:40 +05:30
Kovid Goyal
289028b468 Prevent umask from interfering with tar file extraction
Fixes #5025
2022-04-28 17:21:34 +05:30
Kovid Goyal
2b3be147e6 Allow three combining chars per cell
Makes use of otherwise wasted padding in the CPUCell struct
2022-04-28 09:45:34 +05:30
Kovid Goyal
1a2d9c6fba Fix a couple of issues highlighted by updated mypy 2022-04-28 07:03:35 +05:30
Kovid Goyal
af15b0313a Ensure there are no duplicate watchers for send-text sessions 2022-04-27 22:17:09 +05:30
Kovid Goyal
b080296326 Remove unused code 2022-04-27 21:25:39 +05:30
Kovid Goyal
516e0e8bb3 broadcast kitten: Keep the ghost cursors in target windows as long as broadcast window has focus 2022-04-27 21:24:18 +05:30
Kovid Goyal
57e23bd4e3 Handle zero blink time correctly 2022-04-27 20:37:23 +05:30
Kovid Goyal
59534d620e Use cursor blink interval as TIMEOUT_FOR_SESSION 2022-04-27 20:31:16 +05:30
Kovid Goyal
068b7e8d63 remove unused adjust_line_height() function 2022-04-27 20:21:36 +05:30
Kovid Goyal
2c1edb9611 Merge branch 'docs' of https://github.com/page-down/kitty 2022-04-27 20:18:39 +05:30
pagedown
510022c3c1 Docs: Improve the documentation for kittens
Add more text roles and links.
Add an example that broadcasts only to other windows in the current tab.
Initial capitalization of the key names in the kbd text role.
Add Python type hints for custom kittens.
Note about hyperlink support for ls on macOS.
Add description text for show_key.
2022-04-27 16:14:54 +08:00
Kovid Goyal
627c79ffbb Show detailed traceback when using show_error() 2022-04-26 13:58:57 +05:30
Kovid Goyal
e80cd36237 Show traceback on stderr for action failures 2022-04-26 11:58:17 +05:30
Kovid Goyal
92ebada9a6 ... 2022-04-26 11:15:45 +05:30
Kovid Goyal
462ae3bb58 ... 2022-04-26 11:15:33 +05:30
Kovid Goyal
6d6d9cc26b macOS: Add the macos_colorspace option to control what color space colors are rendered in
Fixed #4686
2022-04-26 11:14:01 +05:30
Kovid Goyal
a36d5dcde1 GLFW API for changing colorspace of windows on cocoa 2022-04-26 10:56:34 +05:30
Kovid Goyal
f9f6f98527 Note that neovim is also getting support for the kitty keyboard protocol 2022-04-26 08:39:44 +05:30
Kovid Goyal
ee94114eb2 Fix #5016 2022-04-26 08:20:24 +05:30
Kovid Goyal
8e98fcf2f6 Merge branch 'docs' of https://github.com/page-down/kitty 2022-04-25 14:16:16 +05:30
pagedown
c4710bf9cb Docs: Add text to the ref text role and minor tweaks
The text will be used when generating the commented configuration file.
Use inline literal to quote codes with spaces, as the `code` text role
will be removed when the commented configuration is generated, making it
indistinguishable from normal text.
2022-04-25 15:36:57 +08:00
pagedown
97a568a405 Docs: Improve the FAQ
Add more text roles and links.
Document `PATH` in the glossary.
2022-04-25 15:36:51 +08:00
pagedown
7bace19aed clone-in-kitty: Skip more SSH env vars to use the correct values 2022-04-25 15:36:43 +08:00
pagedown
7282f1f684 Docs: Improve ssh kitten documentation
Add more text roles.
Put the env example at the end.
The alias syntax of fish and bash requires quotation marks.
Document `SSH_ASKPASS` in the glossary.
2022-04-25 15:36:37 +08:00
Kovid Goyal
9edb772a81 Merge branch 'fix-launch-cwd-oldest' of https://github.com/rafikdraoui/kitty 2022-04-24 22:04:05 +05:30
Rafik Draoui
d123b6c377 Fix launch --cwd=oldest 2022-04-24 13:19:22 -03:00
Kovid Goyal
c9ba3695d3 ... 2022-04-24 15:50:02 +05:30
Kovid Goyal
0ee970b418 --hold is not suitable for replay commands as it outputs to the terminal 2022-04-24 15:37:20 +05:30
Kovid Goyal
8239cb1b5a Merge branch 'docs' of https://github.com/page-down/kitty 2022-04-24 15:36:35 +05:30
pagedown
97caa073b0 Docs: Improvements to the configuration documentation
Slightly adjust and simplify the config option descriptions.
Add more text roles.
Initial capitalized key names.
Change some examples to better demonstrate configurations.
Fix listen_on description.
Also document cmd+c and cmd+v.
Fix doc text role links in commented out configuration file.
2022-04-24 17:32:49 +08:00
pagedown
45bbe17559 Docs: Minor improvements to the kitty cli help documentation
Add some text roles.
Use `kitty --hold`.
Use `appname` and `conf_name`.
`appname` is also applied to the system-wide configuration path.
2022-04-24 17:18:55 +08:00
pagedown
288d16f7be Docs: Minor tweaks to the help documentation of hints kitten 2022-04-24 17:18:45 +08:00
pagedown
ecb60b313e Docs: Improve clone-in-kitty documentation and fix LANG descriptions
Reduce duplicate `launch`.
Add links to python venv and conda.
Adjust the order of env vars, `.._CODE` takes precedence over `.._PATH`.
Add clone-in-kitty env vars to glossary.
Correct the description of the LANG env var.
2022-04-24 17:18:31 +08:00
Kovid Goyal
cf2a20e4ea Merge branch 'fix-f-string' of https://github.com/page-down/kitty 2022-04-24 08:03:51 +05:30
pagedown
bc4f40fea7 ... 2022-04-24 09:34:22 +08:00
Kovid Goyal
102097da5a DRYer 2022-04-23 12:51:02 +05:30
Kovid Goyal
0e3528df14 Merge branch 'feat-macos-open-website' of https://github.com/page-down/kitty 2022-04-23 12:49:21 +05:30
Kovid Goyal
214631c2dd Sort frozenset literals in generated options class definition 2022-04-23 11:23:21 +05:30
pagedown
e919857dfc macOS: Make the global menu shortcut to open kitty website configurable
This allows remapping the key combination shift+cmd+/
2022-04-23 13:22:27 +08:00
pagedown
a5bd1dcb08 Add a new mappable action open_url 2022-04-23 13:17:28 +08:00
Kovid Goyal
37cdaea9ed Also save command history before cloning in bash 2022-04-22 23:40:59 +05:30
Kovid Goyal
6bbaf5f1cf zsh cloning: save history so that it is up-to-date in cloned shell 2022-04-22 23:34:34 +05:30
Kovid Goyal
8808a252ae Merge branch 'docs' of https://github.com/page-down/kitty 2022-04-22 13:29:24 +05:30
pagedown
5f1376b9a0 ... 2022-04-22 15:02:23 +08:00
Kovid Goyal
efa873bf50 DRYer 2022-04-22 10:53:25 +05:30
Kovid Goyal
714461de76 Also ensure oldest cwd request type is honored by modify_argv 2022-04-22 08:40:18 +05:30
Kovid Goyal
b753cf6879 Forgot to implement at_prompt use of reported cwd in modify_argv 2022-04-22 08:25:46 +05:30
Kovid Goyal
27010d0446 Merge branch 'docs' of https://github.com/page-down/kitty 2022-04-21 15:24:18 +05:30
Kovid Goyal
bdc039fbf5 Fix docstring 2022-04-21 14:51:23 +05:30
pagedown
72f92f3174 Docs: Mention again that kitty.conf will be created if not present 2022-04-21 16:45:12 +08:00
Kovid Goyal
8fcd5e40d4 Proper fix for reporting button in drag events to children 2022-04-21 11:41:57 +05:30
Kovid Goyal
b76319b7e8 Update changelog 2022-04-21 11:34:18 +05:30
Kovid Goyal
feeb8f28c4 Fix #4993 2022-04-21 11:00:34 +05:30
Kovid Goyal
55b21b741e Fix a regression in the previous release that caused mouse move events to be incorrectly reported as drag events even when a button is not pressed
Fixes #4992

Will need to investigate a different fix for #4925. xterm does report
the button press that caused the drag to start with move events, so we
will have to track that to match its behavior as glfw does not track it
for us.
2022-04-21 07:53:37 +05:30
Kovid Goyal
6941884221 ... 2022-04-20 13:36:26 +05:30
Kovid Goyal
6174c2008f ... 2022-04-20 10:10:05 +05:30
Kovid Goyal
1a32e79470 ... 2022-04-19 21:14:34 +05:30
Kovid Goyal
dd032db89c Merge branch 'docs' of https://github.com/page-down/kitty 2022-04-19 21:07:03 +05:30
pagedown
f70e0e216c Docs: Show copy button for installation command snippets 2022-04-19 23:28:51 +08:00
Kovid Goyal
51362706d7 Fix closing a tab incorrectly storing the tab to its right in the active tab history 2022-04-19 17:44:01 +05:30
Kovid Goyal
b2c317ebc6 ... 2022-04-19 17:38:16 +05:30
Kovid Goyal
90acbd0dd5 Add a note about ssh kitten colors to FAQ 2022-04-19 15:21:05 +05:30
Kovid Goyal
402fac3edf ... 2022-04-19 10:53:18 +05:30
Kovid Goyal
c4c62c1505 When using --keep-focus ensure active history list is not affected
Fixes #4987
2022-04-18 20:23:15 +05:30
Kovid Goyal
b569c01b49 Fix #4986 2022-04-18 17:04:05 +05:30
Kovid Goyal
c0d9b6e979 Merge branch 'docs' of https://github.com/page-down/kitty 2022-04-18 12:41:05 +05:30
pagedown
b94afbba13 ... 2022-04-18 14:51:15 +08:00
Kovid Goyal
1994c17c75 Note on how clone-in-kitty works 2022-04-18 12:08:32 +05:30
Kovid Goyal
f88a4fe986 ... 2022-04-18 11:13:16 +05:30
Kovid Goyal
25616aaa7b Merge branch 'clone-in-kitty' of https://github.com/page-down/kitty 2022-04-18 10:57:55 +05:30
pagedown
19fdcec358 ... 2022-04-18 11:54:14 +08:00
pagedown
45eb4a0760 clone-in-kitty: Remove duplicate PATH entries in fish implementation 2022-04-18 11:46:29 +08:00
pagedown
9e026983e6 clone-in-kitty: Remove CONDA_PREFIX_* env vars 2022-04-18 11:42:22 +08:00
Kovid Goyal
be0a524e23 Yet another try to get zsh to de-dup $PATH 2022-04-18 07:40:37 +05:30
Kovid Goyal
339af1b4b2 Add more send_text examples 2022-04-17 22:48:03 +05:30
Kovid Goyal
31fda568e4 typeset -U both path and PATH 2022-04-17 21:27:08 +05:30
Kovid Goyal
3efbccc850 ... 2022-04-17 20:56:24 +05:30
Kovid Goyal
93c23f99cb conda activate now works under fish so use it 2022-04-17 20:55:53 +05:30
Kovid Goyal
6590d0690e Document venv and sourcing behavior of clone-in-kitty 2022-04-17 19:51:27 +05:30
Kovid Goyal
230a9f4678 Implement option to control clone sourcing strategies 2022-04-17 19:39:32 +05:30
Kovid Goyal
f2189b3e70 only pass VIRTUAL_ENV if it is currently active 2022-04-17 15:43:57 +05:30
Kovid Goyal
0ee5712e00 Start work on option to control sourcing strategies 2022-04-17 15:30:57 +05:30
Kovid Goyal
f9cad2c4ea Add a note about show__key limitations 2022-04-17 08:45:27 +05:30
Kovid Goyal
4372cf2893 ... 2022-04-17 08:43:23 +05:30
Kovid Goyal
34c18bacd8 Free up memory used to store copy of child env block 2022-04-17 08:35:55 +05:30
Kovid Goyal
3af11e92d6 Ensure the clone launch env var is not set accidentally 2022-04-17 08:29:47 +05:30
Kovid Goyal
74d5f2c259 Dont allow inappropriate types for cloning 2022-04-17 08:19:01 +05:30
Kovid Goyal
291f9e9a5e Make cloning safer
Now env vars are set after shell rc files are sourced. And the clone
request cannot specify the cmdline to execute.
2022-04-17 07:49:58 +05:30
Kovid Goyal
38e1d32065 Actually implement allow_cloning 2022-04-16 22:40:05 +05:30
Kovid Goyal
b45fedd794 Infrastructure for serializing env blocks 2022-04-16 22:16:20 +05:30
Kovid Goyal
df9b13fb74 Ask for confirmation before doing a clone by default 2022-04-16 20:04:26 +05:30
Kovid Goyal
53788c4c89 Get conda working with fish 2022-04-16 15:16:53 +05:30
Kovid Goyal
831043b773 Implement venv detection for fish 2022-04-16 14:45:02 +05:30
Kovid Goyal
1daf745d74 Add explicit support for python and conda venvs to clone-in-kitty
Still needs to be implemented for fish
2022-04-16 13:15:10 +05:30
Kovid Goyal
d6c5b40ead ... 2022-04-16 09:26:53 +05:30
Kovid Goyal
57ea524464 Merge branch 'clone-in-kitty' of https://github.com/page-down/kitty 2022-04-15 20:27:36 +05:30
pagedown
e19ce6cce6 clone-in-kitty: Skip some user-related environment variables 2022-04-15 22:47:57 +08:00
Kovid Goyal
8d4f6681e8 Merge branch 'clone-in-kitty' of https://github.com/page-down/kitty 2022-04-15 19:49:33 +05:30
pagedown
9c855a8377 Skip environment variables such as SSH_TTY when using clone-in-kitty 2022-04-15 19:43:40 +08:00
Kovid Goyal
716813e38a Proper fix for macOS argv[0] 2022-04-15 15:23:45 +05:30
Kovid Goyal
e5322cdc52 Fix clone-in-kitty --env not working over ssh 2022-04-15 15:20:38 +05:30
Kovid Goyal
b5701691c6 Dont clone prompt related env variables
These really shouldnt be exported but people often do
2022-04-15 15:18:43 +05:30
Kovid Goyal
aab9482e31 ... 2022-04-15 15:01:13 +05:30
Kovid Goyal
775584b5a5 Use literal quoting for env vars sent over ssh by clone 2022-04-15 14:34:21 +05:30
Kovid Goyal
25f022cc14 Ensure copy_env is False 2022-04-15 14:04:52 +05:30
Kovid Goyal
981ad88309 Allow nuking expandvars globally 2022-04-15 13:58:01 +05:30
Kovid Goyal
e71b9091a3 Dont rely on env -0 for bash either 2022-04-15 13:33:32 +05:30
Kovid Goyal
bde468594e ssh kitten: handle newlines in env vars 2022-04-15 12:49:35 +05:30
Kovid Goyal
0fcfe104e3 Handle argv[0] being buggered on macOS 2022-04-15 12:25:05 +05:30
Kovid Goyal
4cf54d2dfb Dont rely on env -0 in zsh
zsh outputs env vars in a nice one per line format using $'' encoding.
So exploit it
2022-04-15 12:17:59 +05:30
Kovid Goyal
b866c3e783 Add a zsh test for clone env serialization 2022-04-15 12:04:50 +05:30
Kovid Goyal
c15a31e725 Ensure colors are copied form the correct window when cloning
Also, encapsulate clone command into its own class to ease testing
2022-04-15 11:53:28 +05:30
Kovid Goyal
afaf866b15 Merge branch 'ksi-fish-clone-in-kitty' of https://github.com/page-down/kitty 2022-04-14 19:47:41 +05:30
pagedown
af6b1837cb Minor refactoring for fish clone-in-kitty and allow use without env -0
The builtin option from the latest version of fish is not used for now.
clone-in-kitty has been tested with fish 3.2.x.
BusyBox env does not support the -0 option.
2022-04-14 21:32:28 +08:00
Kovid Goyal
aab6f3e450 ... 2022-04-14 17:28:41 +05:30
Kovid Goyal
829ed0ec0d Fix #4964 2022-04-14 17:20:30 +05:30
Kovid Goyal
d9899cb489 Add missing item to changelog 2022-04-14 15:04:48 +05:30
Kovid Goyal
1a370ea9b6 Put the examples front and center 2022-04-14 07:59:32 +05:30
Kovid Goyal
cc07b1f79d Use 2048 chunk size in fish as well 2022-04-13 22:50:24 +05:30
Kovid Goyal
3ddea42660 ... 2022-04-13 22:45:36 +05:30
Kovid Goyal
236dbd95c9 ... 2022-04-13 22:34:23 +05:30
Kovid Goyal
6b078c4267 Implement clone-in-kitty for the fish shell 2022-04-13 22:33:21 +05:30
Kovid Goyal
1dec5f2e08 Help message for clone-in-kitty 2022-04-13 21:38:33 +05:30
Kovid Goyal
e5f70b7306 ... 2022-04-13 21:22:27 +05:30
Kovid Goyal
e2bb856e41 Document clone-in-kitty 2022-04-13 21:01:45 +05:30
Kovid Goyal
88d597f6b2 Dont define clone-in-kitty in POSIX mode
POSIX mode doesnt allow - in identifiers
2022-04-13 20:55:00 +05:30
Kovid Goyal
d0c0b01724 Get clone-in-kitty working for bash as well 2022-04-13 20:38:04 +05:30
Kovid Goyal
4b7c50518e Fix usage of chunk_num 2022-04-13 20:16:51 +05:30
Kovid Goyal
a1bfcd9fc5 Also transfer env vars when cloning over ssh kitten 2022-04-13 20:08:06 +05:30
Kovid Goyal
eb024fa40a ... 2022-04-13 15:51:00 +05:30
Kovid Goyal
122475ad4d Work on getting cloning working with the SSH kitten
Still need to do environment passing
2022-04-13 15:41:42 +05:30
Kovid Goyal
f7114bc1c5 ... 2022-04-13 15:35:06 +05:30
Kovid Goyal
feea6998f8 Allow using the cwd of the oldest as well as the newest foreground process for launch --cwd 2022-04-13 15:31:17 +05:30
Kovid Goyal
e187110611 Use $PWD instead of pwd -P as we want the exact same cwd not the resolved one when cloning 2022-04-13 15:13:58 +05:30
Kovid Goyal
c19e69855a Start work on a command to clone the current working env into a new kitty window 2022-04-13 15:02:22 +05:30
Kovid Goyal
7788f48dd5 Ensure IFS is correct when iterating over KITTY_SHELL_INTEGRATION 2022-04-12 23:03:05 +05:30
Kovid Goyal
073eecb6bb Also package kitty.termcap 2022-04-12 22:43:22 +05:30
Kovid Goyal
030e7e2db3 termcap should be generated in generate not compile 2022-04-12 22:41:19 +05:30
Kovid Goyal
aead3c1c35 Also generate a termcap database entry when building terminfo 2022-04-12 22:38:52 +05:30
Kovid Goyal
587f44ad4e A working implementation of cwd_of_process for FreeBSD 2022-04-12 22:06:46 +05:30
Kovid Goyal
c9ef4aa8c8 Sessions: Fix os_window_size and os_window_class not applying to the first OS Window
Fixes #4957
2022-04-12 21:38:29 +05:30
Kovid Goyal
aabadd8517 ... 2022-04-12 21:19:28 +05:30
Kovid Goyal
b8866371a3 avoid unneccessary session creation during startup 2022-04-12 21:18:18 +05:30
Kovid Goyal
0f3883af02 ... 2022-04-12 21:13:56 +05:30
Kovid Goyal
5876ce0845 Remove --target-group in favor of the new boolean search expressions with matching on window/tab state 2022-04-12 20:41:17 +05:30
Kovid Goyal
2c72c56e22 Add a couple more states to match against
So we can now select windows/tabs that are not active/focused but are in
the active/focused tab/os window.
2022-04-12 20:35:31 +05:30
Kovid Goyal
f15ce21da1 Markup the --match options help text a bit more 2022-04-12 20:20:29 +05:30
Kovid Goyal
b3fa7310cb Allow matching on window/tab state 2022-04-12 20:14:01 +05:30
Kovid Goyal
ade38870a0 Allow using boolean operators when matching windows or tabs 2022-04-12 19:55:20 +05:30
Kovid Goyal
11bc1b100c Handle <> inside :ref: on the CLI 2022-04-12 19:48:09 +05:30
Kovid Goyal
93a7b220c9 Fix parsing of quoted words 2022-04-12 19:31:56 +05:30
Kovid Goyal
8f92c594f2 Create a search query parser 2022-04-12 19:26:25 +05:30
Kovid Goyal
afebea8635 --tab-title=current should respect any override title set on the tab 2022-04-12 12:53:59 +05:30
Kovid Goyal
5d76cfb578 Remove output start mark from last command output 2022-04-12 08:55:07 +05:30
Kovid Goyal
f085f853bd unactive -> inactive 2022-04-12 06:35:29 +05:30
Kovid Goyal
4ac2312b2a Fix #4954 2022-04-12 06:33:35 +05:30
Kovid Goyal
c6dcbe6521 Dont check if running in kitty when updating
I dont actually know if it causes any problems and its fairly
inconvenient to use another terminal.
2022-04-11 22:07:36 +05:30
Kovid Goyal
49efbf0c6e Remove the code to generate installer.sh from installer.py 2022-04-11 21:35:59 +05:30
Kovid Goyal
1d8d669a43 version 0.25.0 2022-04-11 19:31:45 +05:30
Kovid Goyal
f4d44f30b4 Allow copying current window/tab/os window titles when creating new ones via the launch command. Fixes #4933 2022-04-11 15:38:33 +05:30
Kovid Goyal
4c77b0c562 Fix #4947 2022-04-11 15:00:27 +05:30
Kovid Goyal
2f367fa90c Note that wayst also has graphics protocol support 2022-04-10 07:38:55 +05:30
Kovid Goyal
970ec9c839 Merge branch 'feat-close-other-tabs' of https://github.com/page-down/kitty 2022-04-10 07:17:31 +05:30
pagedown
be52f29792 ... 2022-04-09 18:27:32 +08:00
pagedown
8ae2f06828 Add a new mappable action close_other_tabs_in_os_window 2022-04-09 18:26:31 +08:00
Kovid Goyal
601b2a05ac Add instructions for how to do debug builds 2022-04-09 08:07:29 +05:30
Kovid Goyal
b78fbb4521 Fix #4938 2022-04-08 15:44:33 +05:30
Kovid Goyal
c40ef01445 Fix resizing window that is extra tall/wide because of left-over cells not working reliably
Fixes #4913
2022-04-08 15:41:53 +05:30
Kovid Goyal
ab8a4c6b9f Make per cell bias calculation overridable more easily 2022-04-08 14:52:53 +05:30
Kovid Goyal
dd331ca12e ssh kitten: Allow changing terminal colors when connecting 2022-04-08 12:10:59 +05:30
Kovid Goyal
c3b23679f3 Make some themes code re-useable 2022-04-08 11:33:50 +05:30
Kovid Goyal
d0398dca28 Bash shell integration: Fix yet another inconsistent use of SYS_BASHRC
Why Linux distros are constitutionally incapable of using consistent
names and values for anything is beyond me.

Fixes #4934
Fixes #4935
2022-04-07 17:54:52 +05:30
Kovid Goyal
e66c732b41 Forgot to adjust test for new mouse encoding 2022-04-07 17:23:23 +05:30
Kovid Goyal
e7b216ba68 DRYer 2022-04-07 16:24:57 +05:30
Kovid Goyal
838a85ee8b Fix drag event reporting not specifying the current button.
xterm does this, so in the absence of a spec we copy xterm. Fixes #4925
2022-04-07 16:22:26 +05:30
Kovid Goyal
3ec93e0b4e Fix #4932 2022-04-07 15:29:15 +05:30
Kovid Goyal
e8a9bbb51d Merge branch 'fix-inactive-text-alpha' of https://github.com/page-down/kitty
Fixes #4928
2022-04-07 15:16:26 +05:30
pagedown
25c2a0e241 Update cursor focus flag when preparing to render OS window
Fix inactive text alpha always being applied when starting a program
with cursor hidden via session.
2022-04-07 11:48:53 +08:00
Kovid Goyal
de12dddbef Better error message when ripgrep is not found 2022-04-06 16:38:18 +05:30
Kovid Goyal
24eeeedb1f ... 2022-04-05 16:08:24 +05:30
Kovid Goyal
c2bc24de35 Read appearance setting from desktop portal as well 2022-04-05 15:44:27 +05:30
Kovid Goyal
21446e36c1 Restore invalid LANG after trying to get python to set the locale without it 2022-04-04 20:01:01 +05:30
Kovid Goyal
0f734719e7 ... 2022-04-04 19:09:13 +05:30
Kovid Goyal
f3c96f308d Only do update checking in binary builds
Distro packages and source builds will no longer do update checking.
Remove the packaging defaults instructions since they are not needed
any more. The shell integration scripts shouldnt be installed system
wide anyway.
2022-04-04 16:02:38 +05:30
Kovid Goyal
8ff841f1ac ... 2022-04-04 15:34:07 +05:30
Kovid Goyal
709c4abb53 Do not do automatic upgrades of remote kitty
Can be disruptive, since upgrading outputs to the terminal.
Instead updating can be triggerred manually with kitty update-kitty
2022-04-04 14:51:49 +05:30
Kovid Goyal
c1f06941f5 Do not depend on python to install kitty
Now the installer is implemented as pure POSIX sh. This makes it
possible to install kitty on systems without python, easily.
2022-04-04 13:42:20 +05:30
Kovid Goyal
ecb74aed29 ... 2022-04-04 11:05:36 +05:30
Kovid Goyal
c72154782b Simplify getting released kitty version
Also if fetching release version fails and local version exists, use it
2022-04-04 10:22:25 +05:30
Kovid Goyal
9520006466 Dont use alternate screen as it is too disruptive 2022-04-04 07:53:48 +05:30
Kovid Goyal
40f04284d4 Update the text to reflect current support status 2022-04-04 07:51:22 +05:30
Kovid Goyal
832534aac9 Show installation output in alternate screen 2022-04-03 22:13:02 +05:30
Kovid Goyal
6cc89416ce Implement locking for the kitty bootstrap install 2022-04-03 21:53:23 +05:30
Kovid Goyal
b3fc2cb118 Avoid executing kitty just to read installed kitty version 2022-04-03 20:47:02 +05:30
Kovid Goyal
69efd9801b Add a mention of on-demand kitty in the features list 2022-04-03 20:35:48 +05:30
Kovid Goyal
c07f164154 Implement an option to control the installation of the kitty bootstrap script 2022-04-03 20:30:16 +05:30
Kovid Goyal
a2d1140229 Forgot to run gen-config.py after changing default for confirm_os_window_close 2022-04-03 19:57:54 +05:30
Kovid Goyal
bae9b095b4 Start work on kitty launcher for remote servers
This will automatically download kitty and run it. The ssh kitten
can add it to PATH thereby making kitty available on the remote machine
at low cost.
2022-04-03 15:10:20 +05:30
Kovid Goyal
efe6ff6188 Try to ensure builtin command is used 2022-04-03 12:16:13 +05:30
Kovid Goyal
3257fdf24f Merge branch 'master-macos_option_as_alt_docs' of https://github.com/miguno/kitty 2022-04-01 18:27:14 +05:30
Michael G. Noll
3284d71300 Clarify documentation of macos_option_as_alt
See discussion at https://github.com/kovidgoyal/kitty/issues/4908
2022-04-01 14:52:12 +02:00
Kovid Goyal
cdb3e2c1cd Added a version added note for the ssh kitten 2022-04-01 14:23:31 +05:30
Kovid Goyal
57cffc71b7 Fix building on macOS with a non-framework Python
Fixes #289
2022-04-01 07:39:54 +05:30
Kovid Goyal
a4f1caeb4e Missed a couple of actions invocations 2022-04-01 07:31:10 +05:30
Kovid Goyal
45de091ee6 Use versioned actions as master for setup-python is currently broken 2022-04-01 07:19:41 +05:30
Kovid Goyal
00c1802319 Change the default for confirm_os_window_close
Now that the new ssh kitten makes shell integration easy even on remote
servers, it makes sense to change the default behavior to ask for
confirmation when closing windows not at a shell prompt.
2022-03-31 20:33:58 +05:30
Kovid Goyal
02c2d24360 Merge branch 'feat-vis-sel-chars' of https://github.com/page-down/kitty 2022-03-31 15:15:00 +05:30
pagedown
499c255e81 Use the configured chars when visually selecting windows on stack layout
Also list the current active window in the candidates and gray it out to
make sure the order is consistent.
2022-03-31 17:32:10 +08:00
Kovid Goyal
30cad2e0a6 select_tab: Use stable numbers when selecting the tab
Fixes #4792
2022-03-31 13:16:08 +05:30
Kovid Goyal
6e838b83d8 Add a note about how to use a new target tab to --target-tab docs 2022-03-31 12:52:08 +05:30
Kovid Goyal
5297223ee3 Dont use a blank line for empty subtitle 2022-03-31 12:43:28 +05:30
Kovid Goyal
2340d5be8d Merge branch 'ksi-fish' of https://github.com/page-down/kitty 2022-03-30 18:51:16 +05:30
Kovid Goyal
7de1a145aa Merge branch 'docs' of https://github.com/page-down/kitty 2022-03-30 18:44:41 +05:30
pagedown
32426d61c3 Shell integration: Mark fish prompt on cancel or syntax error
The fish_prompt event will not be fired, even if a new prompt has been
drawn.
2022-03-30 20:49:48 +08:00
pagedown
3ce47660a0 Docs: Add instructions for compiling terminfo under macOS 2022-03-30 20:12:18 +08:00
Kovid Goyal
0dff455ffa declare oldval local 2022-03-30 11:57:46 +05:30
Kovid Goyal
81739288e8 handle multiple trailing spaces and semi-colons 2022-03-30 11:54:06 +05:30
Kovid Goyal
5641668bc1 Bash integration: Handle existing PROMPT_COMMAND ending with a literal newline 2022-03-30 10:44:59 +05:30
Kovid Goyal
5c03a52a0b ... 2022-03-29 15:34:34 +05:30
Kovid Goyal
087b52e3e0 Merge branch 'feat-scroll-prompt-to-top' of https://github.com/page-down/kitty 2022-03-29 15:00:46 +05:30
pagedown
733b8e7c1c Update changelog 2022-03-29 17:01:41 +08:00
pagedown
aa83f42f2d Avoid using already defined shortcuts in examples 2022-03-29 16:45:02 +08:00
pagedown
4ed2854791 Add a new standalone action scroll_prompt_to_top
Refactor clear_terminal to move the functions to Window.
Fix the action not scrolling to the bottom when the screen is scrolled.
2022-03-29 16:42:29 +08:00
Kovid Goyal
73cdd87d91 Fix completion for kitty +open 2022-03-29 13:04:55 +05:30
Kovid Goyal
51c8e3b2c6 Add some more debug about scale changes 2022-03-29 09:47:48 +05:30
Kovid Goyal
e522095fae ... 2022-03-26 07:39:16 +05:30
Kovid Goyal
6fc1226028 Merge branch 'fix-overlay-ready' of https://github.com/page-down/kitty 2022-03-26 07:38:01 +05:30
Kovid Goyal
fb40280d3c Use x-shellscript in addition to x-sh
/etc/mime.types uses x-sh and file --mime-type uses x-shellscript. Sigh.
Fixes #4881
2022-03-26 07:35:42 +05:30
pagedown
746cb3684a Eliminate flickering when hints kitten exits 2022-03-25 22:53:38 +08:00
pagedown
16c6545e93 Report overlay ready in case of no match 2022-03-25 22:52:35 +08:00
Kovid Goyal
0c4b20aa4e typo 2022-03-25 16:30:48 +05:30
Kovid Goyal
15e3e8d8b1 typo 2022-03-25 16:18:00 +05:30
Kovid Goyal
6123de52c2 Merge branch 'fix-macos-drop-files' of https://github.com/page-down/kitty 2022-03-25 07:29:07 +05:30
pagedown
3db0aab24b macOS: Fix the path encoding issue from drag and drop files 2022-03-25 09:14:56 +08:00
Kovid Goyal
ae77f696ce Merge branch 'ssh-kitten' of https://github.com/page-down/kitty 2022-03-24 20:14:22 +05:30
pagedown
98369db7f5 Unset shell integration env var before exec command 2022-03-24 22:01:11 +08:00
pagedown
bc9d6892d4 Clean up login shell environment variable earlier
Does not pollute the environment when executing commands.
2022-03-24 22:00:51 +08:00
pagedown
5c02c370d4 ssh kitten: Allow to configure HOME environment variable
Useful if the user does not have a home directory or the directory is
not writable.
2022-03-24 22:00:41 +08:00
pagedown
b944cdddeb ssh kitten: Fix python bootstrap checking zsh rc files with ZDOTDIR 2022-03-24 22:00:16 +08:00
Kovid Goyal
a1d203d34a Clean up paste_actions docs 2022-03-24 17:35:07 +05:30
Kovid Goyal
e151b8e604 Make detection of pasted URLs a little stricter 2022-03-24 17:30:47 +05:30
Kovid Goyal
3c67e991c2 Allow defining actions to take when pasting
Fixes #4873
2022-03-24 17:22:46 +05:30
Kovid Goyal
cb7aed3234 DRYer 2022-03-24 11:54:13 +05:30
Kovid Goyal
ff80b906d0 Report full traceback when highlighting fails 2022-03-24 11:39:31 +05:30
Kovid Goyal
8300436481 Make setting kitten_result more robust 2022-03-24 11:34:12 +05:30
Kovid Goyal
6b13454091 ... 2022-03-24 09:50:50 +05:30
Kovid Goyal
9eae4ad913 ... 2022-03-24 09:37:13 +05:30
Kovid Goyal
bfb8532c52 Jump through the hoops needed to ensure error reporting is done in case of early termination of kittens that use overlay ready notifications 2022-03-24 08:49:36 +05:30
Kovid Goyal
c41405fd57 Merge branch 'ssh-kitten' of https://github.com/page-down/kitty 2022-03-23 18:19:12 +05:30
pagedown
276ba7754a Add ssh kitten URL test 2022-03-23 20:08:50 +08:00
pagedown
8569be81ea ... 2022-03-23 19:49:34 +08:00
pagedown
a765d551e4 Fix bash integration cwd reporting missing hostname 2022-03-23 19:29:18 +08:00
pagedown
4309aa1ace ssh kitten: Fix SSH URL hostname matching 2022-03-23 18:39:27 +08:00
Kovid Goyal
6ef83a09d3 Avoid clearing the screen when exiting a kitten as its not needed any more 2022-03-23 16:06:38 +05:30
Kovid Goyal
adcc616c92 ... 2022-03-23 16:04:01 +05:30
Kovid Goyal
6dc1617429 Avoid flicker when starting kittens such as the hints kitten
Fixes #4674
2022-03-23 15:55:11 +05:30
Kovid Goyal
1837168b0b Micro optimization 2022-03-23 14:51:22 +05:30
Kovid Goyal
1be1864657 Allow sending more than a screenful of output from a kitten
Also Fixes #4866
2022-03-23 14:33:53 +05:30
Kovid Goyal
49c335972f Update padding color on colorprofile pop 2022-03-23 08:35:36 +05:30
Kovid Goyal
aabc99c7bf ... 2022-03-22 21:12:35 +05:30
Kovid Goyal
7c91dc6183 Merge branch 'docs' of https://github.com/page-down/kitty 2022-03-22 21:07:05 +05:30
pagedown
ff8a99211d Docs: Unify and generalize the terms remote computer, machine and host 2022-03-22 23:26:15 +08:00
pagedown
efda0ea455 Docs: Categorize the ssh kitten configuration options and minor tweaks 2022-03-22 23:25:59 +08:00
Kovid Goyal
906be21b8d Make GNOME detection a bit more tolerant 2022-03-22 11:36:09 +05:30
Kovid Goyal
5a36fbfe7b Only move overlay line/pre-edit position when cursor is visible
This might help with #4849
2022-03-22 10:35:55 +05:30
Kovid Goyal
f9f69a0577 ... 2022-03-22 10:28:41 +05:30
Kovid Goyal
8ccbb96b8d Update changelog for last PR
Fixes #4853
2022-03-22 08:22:15 +05:30
Kovid Goyal
8ae7256fd6 Merge branch 'fix-ime' of https://github.com/page-down/kitty 2022-03-22 08:21:31 +05:30
Kovid Goyal
ba401c19c2 Pass mode when opening existing shm buffer 2022-03-21 16:12:26 +05:30
pagedown
11343d42c3 Wayland: Fix backspace cannot clear the last character of pre-edit text 2022-03-21 18:36:35 +08:00
pagedown
6c628bc594 Wayland: Fix IME is not enabled after switching the focus 2022-03-21 18:36:25 +08:00
Kovid Goyal
f6cbca1aeb ... 2022-03-20 14:50:04 +05:30
Kovid Goyal
51e7c8c136 Move the splitting code into python
Less code and more robust
2022-03-20 14:40:02 +05:30
Kovid Goyal
f952694ffd Merge branch 'macos-multi-dirs-here' of https://github.com/page-down/kitty 2022-03-20 14:12:08 +05:30
pagedown
59afdfc4e9 macOS: Allow the New kitty Here services to open multiple folders 2022-03-20 14:51:47 +08:00
Kovid Goyal
27ec1e4b3c Clean up logic for getting CWD 2022-03-18 12:25:01 +05:30
Kovid Goyal
e791a8a7bb Add the SSH kitten to the Changelog 2022-03-18 10:45:16 +05:30
Kovid Goyal
66fe53ceb1 Merge branch 'docs' of https://github.com/page-down/kitty 2022-03-18 09:36:36 +05:30
Kovid Goyal
de3cc11423 Merge branch 'version-check' of https://github.com/page-down/kitty 2022-03-18 09:35:49 +05:30
pagedown
901eae9604 Docs: no-cwd will not disable fish's built-in CWD reporting 2022-03-18 11:10:21 +08:00
pagedown
1962519666 Only write to the OpenSSH version sentinel file once 2022-03-17 22:23:48 +08:00
pagedown
1e84cbe2ab Check macOS less version 2022-03-17 22:23:37 +08:00
Kovid Goyal
06da2b88ff Also check if states are empty 2022-03-17 17:51:21 +05:30
Kovid Goyal
91b9110dcc Workaround for bug in mutter that caused first OS window to not have functional CSD when launched from activities
Fixes #4802
2022-03-17 17:35:56 +05:30
Kovid Goyal
d57abb56ce Dont use remote cwd as child cwd 2022-03-17 16:42:11 +05:30
Kovid Goyal
f6edb774fc Merge branch 'ssh-cwd' of https://github.com/page-down/kitty 2022-03-17 15:34:11 +05:30
pagedown
ec0f3e9128 Allow *_with_cwd to work with ssh kitten when the shell is configured 2022-03-17 16:40:55 +08:00
Kovid Goyal
6f1404d17b typo 2022-03-17 10:59:41 +05:30
Kovid Goyal
3d9e47c295 typo 2022-03-17 10:52:09 +05:30
Kovid Goyal
afa6128155 Implement --cwd=last_reported 2022-03-17 10:29:31 +05:30
Kovid Goyal
72b1996423 Allow different kinds of cwd requests 2022-03-17 10:24:34 +05:30
Kovid Goyal
6b1a9d635e ... 2022-03-17 09:33:12 +05:30
Kovid Goyal
dea79f16d0 Update the how it works section 2022-03-17 09:31:03 +05:30
Kovid Goyal
2d772d7243 ... 2022-03-16 21:42:37 +05:30
Kovid Goyal
09fb246c8f ... 2022-03-16 21:30:16 +05:30
Kovid Goyal
0a5c16363c Fix continued lines not having their continued status reset on line feed
Fixes #4837
2022-03-16 15:20:25 +05:30
Kovid Goyal
b444f4636e Move another function into bootstrap-utils.sh 2022-03-16 07:37:56 +05:30
Kovid Goyal
2b8acebd6e Make bootstrap.sh even smaller 2022-03-16 07:31:33 +05:30
Kovid Goyal
766010c292 ... 2022-03-16 07:27:52 +05:30
Kovid Goyal
0a2768e496 Split up the bootstrap script to keep its size down 2022-03-16 07:17:12 +05:30
Kovid Goyal
0fd7f3f7b5 Merge branch 'ksi' of https://github.com/page-down/kitty 2022-03-16 07:02:18 +05:30
pagedown
d3472966d3 Fix generating terminfo on NetBSD 2022-03-16 09:15:31 +08:00
pagedown
576ab24609 Preserve line breaks to keep the line numbers in error messages 2022-03-16 08:58:50 +08:00
pagedown
621453b068 ... 2022-03-16 01:33:23 +08:00
pagedown
6638546247 Fix generating terminfo on NetBSD
.terminfo.cdb is the compiled terminfo database file.
Ensure that both .terminfo (dir) and .terminfo.cdb are generated.
2022-03-16 01:04:26 +08:00
pagedown
f99edbae3c Reduce bootstrap script size 2022-03-16 01:04:02 +08:00
pagedown
150bf1a5b0 Reduce bootstrap script length by removing comments and indents
dropbear has 9000 bytes limit on ssh arguments length.
2022-03-16 01:03:51 +08:00
pagedown
91a17e3f0c Fall back to use POSIX SHELL environment variable 2022-03-16 01:03:33 +08:00
pagedown
7160027c14 Use the fish built-in function name to report current working directory
If the feature is enabled by fish in the future, it will override the
built-in one and there will be no duplicate reporting.
2022-03-16 01:03:26 +08:00
pagedown
736d6cf7e6 Simplify fish prompt state checking 2022-03-16 01:03:16 +08:00
Kovid Goyal
82de6a1c56 DRYer 2022-03-15 22:12:49 +05:30
Kovid Goyal
1fbb4f763e Only check OpenSSH version once on a machine if it is new enough 2022-03-15 22:09:40 +05:30
Kovid Goyal
20582431d6 typo 2022-03-15 21:50:55 +05:30
Kovid Goyal
7d9cf0b064 When at shell prompt use reported cwd 2022-03-15 21:16:49 +05:30
Kovid Goyal
a834f221f1 Unquote paths from file:// URLs
The original iTerm spec for this says the urls are supposed to be
percent escaped
2022-03-15 21:07:36 +05:30
Kovid Goyal
c95dca4023 See if we can make the test more robust 2022-03-15 20:38:02 +05:30
Kovid Goyal
c05c0353fd Dont report directory changes during command substitution 2022-03-15 20:34:18 +05:30
Kovid Goyal
e944e2ecae Make a note that cwd reporting is builtin to fish 2022-03-15 20:33:14 +05:30
Kovid Goyal
47b3c37bf0 Add cwd reporting to fish integration 2022-03-15 20:25:39 +05:30
Kovid Goyal
190666dc8a Check that non-ascii paths work in cwd reporting 2022-03-15 20:16:09 +05:30
Kovid Goyal
8c6e7ce61a ... 2022-03-15 20:11:27 +05:30
Kovid Goyal
04ead526b9 Presend data on slave connections since for these SSH does not use the TTY 2022-03-15 20:09:05 +05:30
Kovid Goyal
135fb7e6e4 ... 2022-03-15 20:01:23 +05:30
Kovid Goyal
ce1e22ac95 Have *_with_cwd work with the ssh kitten to open new windows auto-logged into the remote server at the current remote working directory 2022-03-15 19:59:16 +05:30
Kovid Goyal
a216f6bd46 ssh doesnt use askpass even if DISPLAY is set
It only uses askpass if not connected to a terminal
2022-03-15 17:27:57 +05:30
Kovid Goyal
c47e5372b5 native askpass should be used when requested 2022-03-15 15:54:48 +05:30
Kovid Goyal
daa673eff1 Make usage of need_to_request_data clearer 2022-03-15 15:51:49 +05:30
Kovid Goyal
5099dd6aa3 Only use kitty's askpass if DISPLAY is set or OpenSSH is new enough to support SSH_ASKPASS_REQUIRE 2022-03-15 15:47:02 +05:30
Kovid Goyal
f982e754e4 Enable CWD reporting in bash integration 2022-03-15 15:37:17 +05:30
Kovid Goyal
4a1ad7755a Enable CWD reporting in the zsh integration 2022-03-15 14:30:21 +05:30
Kovid Goyal
946589d1f1 ... 2022-03-15 13:18:07 +05:30
Kovid Goyal
7168ceab94 Make askpass a choice var 2022-03-15 13:14:53 +05:30
Kovid Goyal
868d57b818 ... 2022-03-15 12:00:17 +05:30
Kovid Goyal
6057c57ea4 Fix garbage removal pafter SSH failure not working on macOS when run more than once 2022-03-15 11:57:28 +05:30
Kovid Goyal
e8437fd435 Make reading garbage more robust 2022-03-15 11:51:46 +05:30
Kovid Goyal
9e317971b4 Simplify garbage clear logic 2022-03-15 11:41:59 +05:30
Kovid Goyal
bdb55a02fa ... 2022-03-15 11:40:14 +05:30
Kovid Goyal
02e062af65 Add a mappable action to cleanup shared SSH connections 2022-03-15 11:34:56 +05:30
Kovid Goyal
54c5faa12d Simplify hostname matching
Now that we load the opts upfront, we can have load_config return the
final opts object itself
2022-03-15 11:25:21 +05:30
Kovid Goyal
9b0bd81661 Ignore hostname directives when using overrides 2022-03-15 10:38:03 +05:30
Kovid Goyal
52da68876d Use a zero mtime for data.sh
Its a temp file and that way no chance of the time being in the future
2022-03-15 10:30:15 +05:30
Kovid Goyal
2780630a18 Suppress STDERR for tar 2022-03-15 10:28:14 +05:30
Kovid Goyal
8f77cc65e2 Use a full window object rather than a child PID to get cwd
This allows implementation of more sophisticated algorithmhs
for getting the cwd
2022-03-15 07:58:57 +05:30
Kovid Goyal
7224363639 ... 2022-03-15 07:27:25 +05:30
Kovid Goyal
43851fc1c4 Ignore OSC 6 2022-03-14 22:04:38 +05:30
Kovid Goyal
e33bf11012 Store last reported CWD via OSC 7
Can be used with shell integration to give more accurate
cwd detection and also combined with the ssh kitten
to magically open new windows connected to remote servers
at the correct cwd on the remote server instantly.
2022-03-14 22:00:41 +05:30
Kovid Goyal
0e21376538 Update changelog
Fixes #4831
2022-03-14 20:20:33 +05:30
Kovid Goyal
7a463c1240 Merge branch 'fix-diff-kitten' of https://github.com/page-down/kitty 2022-03-14 20:01:36 +05:30
pagedown
8e03da855f diff kitten: Fix wrong number of scrolling lines after context switch 2022-03-14 20:29:15 +08:00
Kovid Goyal
942881d1b7 Add a ntoe about the importance of askpass 2022-03-14 16:44:12 +05:30
Kovid Goyal
2a50203fcd ... 2022-03-14 13:44:18 +05:30
Kovid Goyal
08ce8ffa54 py2 compat 2022-03-14 12:36:43 +05:30
Kovid Goyal
90561682cf Make using kitty askpass optional 2022-03-14 11:38:31 +05:30
Kovid Goyal
71027e74e0 py2 compat 2022-03-14 11:28:46 +05:30
Kovid Goyal
be8bfbe370 Refactor launch sequence in preparation for optionising it 2022-03-14 10:40:07 +05:30
Kovid Goyal
f7e4447b66 Dont transmit sensitive data unless actually needed 2022-03-13 15:43:14 +05:30
Kovid Goyal
df4e58bc39 Improve disposal of garbage data on ssh failure 2022-03-13 15:37:46 +05:30
Kovid Goyal
441ea7d696 askpass.py should be executable 2022-03-13 14:49:36 +05:30
Kovid Goyal
2b06ca5e1a ssh kitten: Send data without a roundtrip
Send data to the remote side without waiting for a data request. Avoids
an extra roundtrip during initialization.
2022-03-13 13:55:30 +05:30
Kovid Goyal
434ef97952 Dont need atexit anymore since cleanup only closes tty_fd 2022-03-13 13:55:30 +05:30
Kovid Goyal
e1504c4775 Avoid needing to initialize tty state in bootstrap scripts 2022-03-13 13:55:30 +05:30
Kovid Goyal
74f0057ec8 Dont change tty state to raw
Just turn off echo. Makes the code much simpler, can just use shell
builtin POSIX read function.
2022-03-13 13:55:30 +05:30
Kovid Goyal
7cd74cb852 Allow writing memoryview to child 2022-03-13 13:55:30 +05:30
Kovid Goyal
b2e74e4830 Switch to using the hostname from the CLI for settings
This removes the need to wait for data from the remote machine
before sending data to it.
2022-03-13 13:55:30 +05:30
Kovid Goyal
f54a3e8036 Make requesting data optional 2022-03-13 13:55:30 +05:30
Kovid Goyal
f3088c5646 Only try to chmod runtime dir if actually needed 2022-03-13 13:45:03 +05:30
Kovid Goyal
db00adaf69 macOS: Use the POSIX user cache dir as the runtime dir
It's path length is 49 chars which is still too long, but it is at least
well defined.
2022-03-13 06:27:41 +05:30
Kovid Goyal
ea74add814 ssh kitten: Restore terminal state after running ssh 2022-03-12 15:48:52 +05:30
Kovid Goyal
ba1b3c3c2d Fix mypy failures 2022-03-12 08:28:43 +05:30
Kovid Goyal
d5c48ddb94 Allow inputting fingerprint check 2022-03-12 08:23:10 +05:30
Kovid Goyal
9687318b22 Retry reading HEAD commit 2022-03-12 08:14:06 +05:30
Kovid Goyal
f6fb36c58a Merge branch 'fix-icat_typeerror_on_print_window_size' of https://github.com/nertro/kitty 2022-03-12 08:08:32 +05:30
Mena Jacobs
c8a258f36b Fix icat fail on mac with --print-window-size
passing tty as TextIOWrapper to screen_size_function results in a TypeError since the screen_size_function expects an int. calling fileno() on tty gives us the desired int. This also fixes the kitty matplotlib backend, if MPLBACEND_KITTY_SIZING is not set to manual
2022-03-12 02:17:01 +01:00
Kovid Goyal
14e0b01b40 Echo asterisks when inputting the password 2022-03-11 16:44:08 +05:30
Kovid Goyal
76de99a5a8 Use a custom askpass implementation
Keeps OpenSSH from using the tty hopefully allowing us to start sending
data without needing to wait for the remote side to request it.
2022-03-11 16:44:08 +05:30
Kovid Goyal
8613c6e1cd ... 2022-03-11 16:44:08 +05:30
Kovid Goyal
5bb6b29ca3 Allow controlling the prompt used to input the password 2022-03-11 16:44:08 +05:30
Kovid Goyal
61558d518e Get askpass basically working 2022-03-11 16:44:08 +05:30
Kovid Goyal
3b724c8415 Start work on SSH askpass implementation 2022-03-11 16:44:08 +05:30
Kovid Goyal
910565aa7c Mention the shell in the error message 2022-03-11 15:52:51 +05:30
Kovid Goyal
c7a2e060e3 Merge branch 'ksi-ssh' of https://github.com/page-down/kitty 2022-03-11 15:51:16 +05:30
pagedown
740e787f54 ... 2022-03-11 17:28:29 +08:00
pagedown
bd87d50948 Remove the ssh folder in cache_dir 2022-03-11 16:47:03 +08:00
pagedown
d3c0c422a5 Add user uid suffix to tmp runtime dir symlink 2022-03-11 15:42:52 +08:00
pagedown
72718cbab7 Remove login shell name suffix restriction
Some shells do not end with sh, consistent with the python bootstrap
implementation.
2022-03-11 15:42:45 +08:00
pagedown
ea28951e0e Fall back to run the shell without -l option 2022-03-11 15:42:34 +08:00
pagedown
39a78f6be3 Explicitly set STDOUT to binary mode 2022-03-11 15:42:23 +08:00
pagedown
fe8aaca320 Use POSIX LOGNAME environment variable 2022-03-11 15:42:09 +08:00
pagedown
f5cc58ea9d Unset the safe source function in sh script and minor refactoring 2022-03-11 15:41:56 +08:00
Kovid Goyal
44992452cf Fix #4815 2022-03-11 09:42:54 +05:30
Kovid Goyal
f080871911 Update the idiotic design symlink atomically 2022-03-11 07:54:16 +05:30
Kovid Goyal
6789eb88e2 Fix replacement of symlinks to directories 2022-03-11 07:51:50 +05:30
Kovid Goyal
1a97be4a25 Merge branch 'fix-pkg-terminfo' of https://github.com/page-down/kitty 2022-03-11 07:39:25 +05:30
Kovid Goyal
c9ef5c0715 Avoid a race in creating the idiotic_design symlink 2022-03-11 07:37:44 +05:30
pagedown
0a9f8d4f6a Add terminfo src file when packaging
ssh kitten needs to send this file.
2022-03-11 08:35:10 +08:00
Kovid Goyal
cac2c153c2 Fix #4809 2022-03-10 22:55:47 +05:30
Kovid Goyal
f2d6ba8775 Workaround for socket path too long on macOS 2022-03-10 22:50:26 +05:30
Kovid Goyal
92abaad22c Handle cross filesystem untarring in bootstrap.py 2022-03-10 22:43:54 +05:30
Kovid Goyal
3df0194f6e Use a well known runtime dir if XDG_RUNTIME_DIR not available 2022-03-10 22:18:35 +05:30
Kovid Goyal
f774841ee0 More robust copying of terminfo
Dont use symlinks in the tarball, instead create them
in the bootstrap scripts, only if needed.
2022-03-10 21:08:20 +05:30
Kovid Goyal
a9de91087a Add a comment explaining why we dont commit if resized 2022-03-10 20:47:45 +05:30
Kovid Goyal
76a536ece0 On macOS always use the cache dir a runtime dir
Also make the constant parts of the socket name shorter
as there is a 104 bytes limit on it in macOS SSH.
2022-03-10 20:12:40 +05:30
Kovid Goyal
719339e116 Output debug info about CSD rendering 2022-03-10 19:45:31 +05:30
Kovid Goyal
1c3910de5c Output debug info about resized being called 2022-03-10 19:32:17 +05:30
Kovid Goyal
39d3ef6fe8 Output debug for decoration configure when it happens 2022-03-10 17:59:54 +05:30
Kovid Goyal
064d0fa6f1 Merge branch 'wayland-fixes' of https://github.com/Nefsen402/kitty 2022-03-10 17:37:29 +05:30
Alexander Orzechowski
99a5843595 wayland: Don't commit the surface on a resize
This fixes weirdness with GNOME.

On a resize, we will schedule a new frame anyway which will commit
the wl_surface for us anyway. If we don't resize, we'll commit
to stay true to the wayland spec.
2022-03-10 06:02:22 -05:00
Alexander Orzechowski
39e75e39e8 wayland: Correctly handle xdg decoration configures
Much like toplevel configures, xdg decoration configures only take
effect once the xdg shell configure comes through. Also,
let's get rid of some double computations because we unified the code
paths.
2022-03-10 06:01:50 -05:00
Alexander Orzechowski
322a80e76e wayland: track configures through a bit field
This will let us add more stuff later. Also, it's a better spot
to put the bit field outside of the {current, pending} struct as
the bit field doesn't make any sense if it's part of the `current`
values.
2022-03-10 05:56:56 -05:00
Alexander Orzechowski
d4b048735d wayland: Fix CSD size calculations
This is a partial revert of e359094cff

window->wl.{width, height} and window->wl.current.{width, height}
represent different things. The former represents the actual size of the
egl surface viewport used for terminal rendering. The former represents
the size of the entire window including the CSD (client side decorations).

These numbers should be the same if CSD is disabled.
2022-03-10 05:56:56 -05:00
Kovid Goyal
c23e04fd03 DRYer 2022-03-10 15:37:10 +05:30
Kovid Goyal
f67009f554 Flush the write to shm explicitly 2022-03-10 15:01:58 +05:30
Kovid Goyal
4013544efb Cleanup control master sockets in parallel 2022-03-10 12:59:38 +05:30
Kovid Goyal
31d9db7e74 Use XDG_RUNTIME_DIR to store control master sockets
On Linux this has the advantage that the dir is auto cleaned on reboot
2022-03-10 12:51:21 +05:30
Kovid Goyal
384c56f834 When using control masters also enable keep alives
Prevents the master connection from becoming stale. Use ssh protocol
keep alives rather than tcp ones as tcp ones are often blocked by
firewalls and also spoofable.
2022-03-10 11:48:36 +05:30
Kovid Goyal
3282c8200d Dont print to stderr when shutting down master connections at exit 2022-03-10 11:22:46 +05:30
Kovid Goyal
ffbc9174f8 We dont change shell rc files anymore 2022-03-10 11:01:59 +05:30
Kovid Goyal
dceb00f669 typo 2022-03-10 11:00:58 +05:30
Kovid Goyal
920086ae88 unlink SHM file at end of each individual test 2022-03-10 10:57:36 +05:30
Kovid Goyal
a1311a2332 ... 2022-03-10 10:48:08 +05:30
Kovid Goyal
4c392426f6 Get shared memory based data transfer working 2022-03-10 10:46:04 +05:30
Kovid Goyal
4528173ff5 Max length for SHM names on Apple is 30!
Bloody CrApple
2022-03-10 07:37:57 +05:30
Kovid Goyal
d3b63a9c45 Cleanup attribute access 2022-03-10 07:30:05 +05:30
Kovid Goyal
c30a249cf6 Use longer random names for SHM to make double unlink safe 2022-03-10 07:24:39 +05:30
Kovid Goyal
20962d989f Use POSIX shm to pass ssh data to kitty 2022-03-10 06:55:21 +05:30
Kovid Goyal
a1e4b19486 ... 2022-03-10 06:34:50 +05:30
Kovid Goyal
fd0262413e Access to POSIX shared memory from Python 2022-03-10 06:30:03 +05:30
Kovid Goyal
aa4d36cc57 Use secrets to generate the random password 2022-03-10 06:11:58 +05:30
Kovid Goyal
8d119f06b3 Force restrictive permissions on file used to pass ssh data to kitty 2022-03-10 05:30:47 +05:30
Kovid Goyal
708c5126b9 Return the data cookie settings when generating the bootstrap script 2022-03-09 22:18:04 +05:30
Kovid Goyal
577de9f746 ssh kitten: Start work on connection sharing
Basic sharing works. Now investigate if we can eliminate the round-trip
latency by transmitting the data without waiting for the start message
when using a shared connection
2022-03-09 19:27:38 +05:30
Kovid Goyal
38a70f5b51 ... 2022-03-09 15:31:19 +05:30
Kovid Goyal
118306a9ff Avoid an extra process hanging around when running the interpreter on the remote machine 2022-03-09 15:29:57 +05:30
Kovid Goyal
704717ff1d Handle case of tty not being OK 2022-03-09 15:27:24 +05:30
Kovid Goyal
405c472d13 Cleanup previous PR 2022-03-09 14:59:08 +05:30
Kovid Goyal
17c994db57 Merge branch 'ksi-ssh' of https://github.com/page-down/kitty 2022-03-09 14:39:28 +05:30
Kovid Goyal
e227264bad Nicer error message when failing to load FreeType face 2022-03-09 14:28:38 +05:30
Kovid Goyal
8751ea79e7 Clear python error flag when loading fallback face fails 2022-03-09 14:16:56 +05:30
pagedown
3b3ffa7455 Docs: Update support for non-POSIX shell tcsh (csh) 2022-03-09 16:12:28 +08:00
pagedown
4322825ac7 Add support for sh that does not support the -l login shell option
FreeBSD sh does not support using the -l option to run as a login shell.
2022-03-09 16:12:20 +08:00
pagedown
d29fa7b382 ssh kitten: Add FreeBSD tcsh (csh) support
Replace line feeds and exclamation marks for tcsh compatibility.
2022-03-09 16:11:58 +08:00
Kovid Goyal
08bb63fa92 Ensure tdir is deleted on exit 2022-03-09 13:24:24 +05:30
Kovid Goyal
5dde31f80c DRYer 2022-03-09 11:52:36 +05:30
Kovid Goyal
3bb869f725 Also test launching of bootstrap.py 2022-03-09 11:32:14 +05:30
Kovid Goyal
11686b90f7 ... 2022-03-09 11:30:34 +05:30
Kovid Goyal
6df78fa67c Add tests with different shells used as launchers 2022-03-09 11:29:06 +05:30
Kovid Goyal
53b1607c4d When testing ssh kitten launch the bootscrapt script the same way sshd does it 2022-03-09 11:25:02 +05:30
Kovid Goyal
2341a27f63 ... 2022-03-08 23:05:42 +05:30
Kovid Goyal
0661caf9da DRYer 2022-03-08 23:04:58 +05:30
Kovid Goyal
5b5bd77c53 ssh kitten: Fix not working when user's login shell is fish 2022-03-08 22:39:31 +05:30
Kovid Goyal
d36f6b39c7 Fix previous PR 2022-03-08 21:29:09 +05:30
Kovid Goyal
b2a5b07f92 Merge branch 'ksi' of https://github.com/page-down/kitty 2022-03-08 21:29:05 +05:30
pagedown
46889a3a32 Fix shell integration being disabled with one invalid option 2022-03-08 23:42:41 +08:00
Kovid Goyal
958ebb72a6 DRYer 2022-03-08 20:48:37 +05:30
Kovid Goyal
febc5c53a6 Get the python interpreter working with non-POSIX login shells 2022-03-08 20:44:29 +05:30
Kovid Goyal
6e921300e2 Let ssh concat the cli args for us 2022-03-08 19:49:25 +05:30
Kovid Goyal
e167dec8a9 Note that on FreeBSD the login shell needs to be changed as well 2022-03-08 19:31:02 +05:30
Kovid Goyal
0019f07cc0 Fix interpreter opt target 2022-03-08 19:25:11 +05:30
Kovid Goyal
20fc1e6b0c Add a note about BSD servers 2022-03-08 18:23:44 +05:30
Kovid Goyal
908946a067 Merge branch 'ksi-ssh' of https://github.com/page-down/kitty 2022-03-08 11:37:40 +05:30
pagedown
f1df7b1c1f Use Python in preference to Perl 2022-03-08 13:59:32 +08:00
Kovid Goyal
65b9c69bd8 Use python in preference to perl
I know how to make python code robust, no clue about perl.
2022-03-08 11:05:02 +05:30
Kovid Goyal
b91e47c3b6 Merge branch 'ksi-ssh' of https://github.com/page-down/kitty 2022-03-08 10:30:11 +05:30
pagedown
0e3a11c53b Use Perl in bootstrap.sh if available 2022-03-08 12:28:09 +08:00
pagedown
53d8d2aaad Use the more explicit b64encode
Fix using ssh kitten to connect to OpenBSD.
2022-03-08 12:19:57 +08:00
Kovid Goyal
84303cbf2e Use head -c only if available 2022-03-07 21:48:06 +05:30
Kovid Goyal
5e457da30b Only use bz2 compression if bzip2 is available 2022-03-07 20:44:26 +05:30
Kovid Goyal
855718b179 ssh kitten: match hostnames against both remote hostname and hostname used on the command line to connect to it 2022-03-07 20:32:02 +05:30
Kovid Goyal
d037c0b0fc OpenBSD compat 2022-03-07 20:14:02 +05:30
Kovid Goyal
90f5937413 Forgot to use the actual python variable 2022-03-07 19:41:18 +05:30
Kovid Goyal
cbbea37b0c ... 2022-03-07 19:37:30 +05:30
Kovid Goyal
4d74b23f20 Merge branch 'ksi-bash' of https://github.com/page-down/kitty 2022-03-07 19:26:56 +05:30
Kovid Goyal
79dd26a43a ... 2022-03-07 18:38:06 +05:30
Kovid Goyal
259e3076fa Ensure python is searched for only once 2022-03-07 18:36:56 +05:30
Kovid Goyal
f2cb2422f0 Use python when base64 is not available 2022-03-07 18:34:02 +05:30
pagedown
4aea64261e ... 2022-03-07 20:56:02 +08:00
pagedown
cf0cd9acd9 Remove trailing semicolons 2022-03-07 20:53:35 +08:00
pagedown
b4b0bdc853 DCS @ kitty-print should be output to STDERR 2022-03-07 20:49:18 +08:00
pagedown
0b2837fcfa Fix bash integration debug log printing 2022-03-07 20:43:14 +08:00
Kovid Goyal
f20ad7df01 Better error message when base64 is not found 2022-03-07 18:12:01 +05:30
Kovid Goyal
c9071a66ca ssh kitten: Add an option to change the cwd at login 2022-03-07 12:24:37 +05:30
Kovid Goyal
b0ea960159 Dont use --no-same-owner when extracting tar archives
This flag is missing on OpenBSD and is the default on other platforms
when extracting as non-root. Make the uname/gname taringo field empty so
when extracting as root files will always be owned by root.
2022-03-07 12:10:45 +05:30
Kovid Goyal
0e7f1d60d6 Have bootstrap.sh preserve file permissions when untarring 2022-03-07 11:45:14 +05:30
Kovid Goyal
03c79502f9 Use uname -m for hostname in bootstrap.sh if needed 2022-03-07 11:24:44 +05:30
Kovid Goyal
d4d4e00f9d Merge branch 'ksi' of https://github.com/page-down/kitty 2022-03-07 11:18:15 +05:30
Kovid Goyal
b52e5e795e Dont use a double quote in bootstrap.py 2022-03-07 11:11:10 +05:30
Kovid Goyal
68df13d3fe Fix recursive definitions in env vars not expanded 2022-03-07 11:07:26 +05:30
pagedown
5f3d90e411 ... 2022-03-07 12:39:49 +08:00
pagedown
1b68e41db4 Remove the fish integration prompt end B prompt marking
Currently kitty does not use the B prompt marking.
This is consistent with the zsh and bash implementations.
Improve compatibility with most user configurations.
2022-03-07 12:12:52 +08:00
pagedown
817ac82968 Detect SSH sessions without affecting performance
Exclude local runs by KITTY_PID.
Check KITTY_WINDOW_ID to detect connections via ssh kitten.
Check SSH via who -m with the integration manually installed and sudo.
2022-03-07 12:00:55 +08:00
pagedown
43fd8cb13c Unify exec function definition order 2022-03-07 11:36:52 +08:00
pagedown
397fbe7ad3 Avoid unnecessary which and fix typos 2022-03-07 11:30:12 +08:00
pagedown
3095e7a256 Remove trailing semicolons 2022-03-07 11:25:22 +08:00
pagedown
df9e893cbe Explicitly use builtin and command 2022-03-07 11:24:18 +08:00
Kovid Goyal
6ff69c88df ... 2022-03-07 06:58:14 +05:30
Kovid Goyal
6e4d3c98da Ensure pw file is cleaned up on kitten exit
This way it is cleaned up even if ssh fails
2022-03-07 06:54:11 +05:30
Kovid Goyal
2404eba11f ssh kitten: Passthrough to ssh if run outside of kitty
Also, ensure that the ssh data request is only served if it is received
over the tty of the correct kitty window.
2022-03-07 06:48:18 +05:30
Kovid Goyal
e5c57a679d Some more docs for the SSH kitten 2022-03-07 06:26:50 +05:30
Kovid Goyal
33de55540b 2nd which unneccesary 2022-03-06 22:25:03 +05:30
Kovid Goyal
99e1605bba Use the correct which implementation
Same one as used by child.py
2022-03-06 21:54:56 +05:30
Kovid Goyal
95c4e26b24 macOS: When using Apple's less as the pager for viewing scrollback strip out OSC codes as Apple's less cant parse them
Fixes #4788
2022-03-06 21:52:59 +05:30
Kovid Goyal
795953a341 get rid of the who -m ssh session test
I dont like executing a binary in a fast startup script. Lets see
if there are a lot of sessions in the wild that dont use the SSH_TTY
env var
2022-03-06 15:47:08 +05:30
Kovid Goyal
75d5e386d3 Make the integration tests more strict
Actually assert that there are/are not prompt marking bytes in the data
received by the pty
2022-03-06 15:35:24 +05:30
Kovid Goyal
00d2a8527f Fix parsing of env vars in bootstrap.py 2022-03-06 15:05:40 +05:30
Kovid Goyal
a1ade8fc42 Dont use format strings in bootstrap.py 2022-03-06 14:47:44 +05:30
Kovid Goyal
7f9fec061a bootstrap.py is now tested the same as bootsstrap.sh 2022-03-06 14:26:15 +05:30
Kovid Goyal
ec782d3296 More work on bootstrap.py 2022-03-06 13:23:30 +05:30
Kovid Goyal
2444864508 Make compiling of terminfo atomic 2022-03-06 13:04:57 +05:30
Kovid Goyal
1ccc50b21b More work on bootstrap.py 2022-03-06 12:55:36 +05:30
Kovid Goyal
4552a474b7 Make the no-integration test more comprehensive and stronger 2022-03-06 12:23:11 +05:30
Kovid Goyal
85c6d8f16e More robust check for exec -a support 2022-03-06 12:20:06 +05:30
Kovid Goyal
14d391cc2e Check $pipestatus for zsh as well 2022-03-06 11:43:33 +05:30
Kovid Goyal
8fe87a0df5 Search PATH when executing login shell 2022-03-06 11:13:40 +05:30
Kovid Goyal
ff01df3b16 Dont byte compile the bootstrap script during build 2022-03-06 10:54:58 +05:30
Kovid Goyal
c713dc0ca8 Allow using a --kitten argument with the ssh kitten to make it easier to override settings from ssh.conf 2022-03-06 10:07:46 +05:30
Kovid Goyal
31ea5d74a7 Exclude junk directories when adding user files as well 2022-03-06 10:00:02 +05:30
Kovid Goyal
31b01d42c0 use inherited instead of inherit 2022-03-06 09:54:33 +05:30
Kovid Goyal
482b853908 Add a note about typing before the prompt being lost 2022-03-05 15:49:21 +05:30
Kovid Goyal
675411df85 Start work on python bootstrap 2022-03-05 15:43:55 +05:30
Kovid Goyal
f94d33fa6a ... 2022-03-05 15:29:09 +05:30
Kovid Goyal
7fe110dff1 Move code to initialize TTY into its own function 2022-03-05 15:16:04 +05:30
Kovid Goyal
591f711886 DRYer 2022-03-05 15:10:09 +05:30
Kovid Goyal
1dc7fc8ac7 Note that zsh's read function is terminally broken 2022-03-05 14:56:57 +05:30
Kovid Goyal
ffa79d731c Allow configuring the ssh kitten via env vars 2022-03-05 12:29:55 +05:30
Kovid Goyal
01dd0416ac Add an option to control the login shell 2022-03-05 11:16:15 +05:30
Kovid Goyal
0bd1676978 Some cleanup 2022-03-05 10:58:42 +05:30
Kovid Goyal
f3407959a6 Start work on python bootstrap script 2022-03-05 10:54:21 +05:30
Kovid Goyal
30e635a934 Use zsh's builtin functions to do I/O with TTY
Far more robust than the system tools
2022-03-05 10:31:00 +05:30
Kovid Goyal
f6706a55ec ... 2022-03-05 09:20:08 +05:30
Kovid Goyal
f0b79f88b2 Merge branch 'fix-copy-or-interrupt' of https://github.com/page-down/kitty 2022-03-05 08:08:40 +05:30
Kovid Goyal
262ba0197d Allow using - as a synonym for /dev/stdin when reading the config 2022-03-05 08:07:32 +05:30
pagedown
31c2447fb8 Fix not scrolling to the bottom when an interrupt is sent
Replicates the same side effects of keyboard key event.
2022-03-05 10:25:49 +08:00
Kovid Goyal
54a4ebfe48 Merge branch 'dte-kbd-proto' of https://github.com/craigbarnes/kitty 2022-03-05 07:51:13 +05:30
Craig Barnes
106c7a1706 Mention dte text editor in list of apps in docs/keyboard-protocol.rst 2022-03-04 23:56:17 +00:00
Kovid Goyal
e469b46ce5 Avoid calling who is either of SSH_TTY or SSH2_TTY are defined 2022-03-04 21:40:24 +05:30
Kovid Goyal
0c79561977 Fix the ssh_copy test on macOS 2022-03-04 21:31:13 +05:30
Kovid Goyal
9e82397de9 Merge branch 'test-ksi-fish' of https://github.com/page-down/kitty 2022-03-04 21:22:02 +05:30
pagedown
7c166e2194 Try to make the fish vi cursor test more stable 2022-03-04 23:46:39 +08:00
Kovid Goyal
0a2b09da40 Prevent inject loop 2022-03-04 21:12:41 +05:30
Kovid Goyal
c02d578468 Nicer error message if bash version < 4
macOS has bash 3
2022-03-04 20:59:22 +05:30
Kovid Goyal
e990d233e5 ooops 2022-03-04 20:54:26 +05:30
Kovid Goyal
a7cdcfcc16 Bash integration: Prevent shell integration code from running twice if user enables both automatic and manual integration 2022-03-04 20:48:57 +05:30
Kovid Goyal
75224e1661 Merge branch 'ksi-bash' of https://github.com/page-down/kitty 2022-03-04 20:37:05 +05:30
pagedown
d236b34fd4 Shell integration: Fix running bash non-interactively
In POSIX mode, bash does not perform ENV with non-interactive shell, so
the mode cannot be recovered. Check the arguments and do not change the
execution environment.
2022-03-04 19:47:44 +08:00
pagedown
c8313409ff Explicitly call bash builtin 2022-03-04 19:36:38 +08:00
Kovid Goyal
6cba8e0166 remove unused variable 2022-03-04 14:52:55 +05:30
Kovid Goyal
0067726bbe spelling 2022-03-04 13:52:33 +05:30
Kovid Goyal
ce620cec0a Use a memoryview to avoid memcopies 2022-03-04 13:41:23 +05:30
Kovid Goyal
e797ba4800 Allow matching on hostname and username 2022-03-04 12:42:31 +05:30
Kovid Goyal
fe27ee2d79 Allow configuring the interpreter to use on the remote host 2022-03-04 12:18:27 +05:30
Kovid Goyal
95efeee7de Also return user name when requesting bootstrap data 2022-03-04 12:16:45 +05:30
Kovid Goyal
da30536709 bash/zsh integration: Show hostname in title when in an SSH session 2022-03-04 11:13:59 +05:30
Kovid Goyal
108ccffcd8 assume grep is present 2022-03-04 10:13:23 +05:30
Kovid Goyal
c3ce0c26e7 ... 2022-03-04 10:01:33 +05:30
Kovid Goyal
e0c984046c EXEC_CMD must come after login_shell detection since it can use login_shell 2022-03-04 10:00:12 +05:30
Kovid Goyal
64d6337612 ... 2022-03-04 09:54:17 +05:30
Kovid Goyal
603684211f bootstrap should not fail if no tty present 2022-03-04 08:30:23 +05:30
Kovid Goyal
499b30d175 Allow specifying multiple matching hostnames 2022-03-04 07:23:35 +05:30
Kovid Goyal
3f3efab221 Fix hostname detection when hostname binary not present 2022-03-04 07:22:59 +05:30
Kovid Goyal
ef9adc92c8 Note on how to use include for DRYer config 2022-03-04 07:13:56 +05:30
Kovid Goyal
ad7c251f56 Fix ln failing if link exists 2022-03-04 07:08:04 +05:30
Kovid Goyal
1c0d254ec6 More ssh kitten docs 2022-03-04 07:03:38 +05:30
Kovid Goyal
f1ce8c0e8a Add some docs for the ssh kitten 2022-03-03 22:37:24 +05:30
Kovid Goyal
1c558be524 Port HISTFILE fix to ssh bash launcher 2022-03-03 21:24:45 +05:30
Kovid Goyal
7d653cb7bf Allow copying the same file to multiple locations 2022-03-03 21:24:45 +05:30
Kovid Goyal
b4cc38a1d9 Fix symlink extraction over ssh 2022-03-03 21:24:45 +05:30
Kovid Goyal
8867317b6a Allow transferring files outside of HOME 2022-03-03 21:24:45 +05:30
Kovid Goyal
4b6bfaffba More work on ssh copy 2022-03-03 21:24:45 +05:30
Kovid Goyal
fadae42715 Work on parsing copy instructions 2022-03-03 21:24:45 +05:30
Kovid Goyal
77c9affc00 Start work on copy instructions 2022-03-03 21:24:45 +05:30
Kovid Goyal
e5ba15949b $size doesnt need to be global 2022-03-03 21:24:45 +05:30
Kovid Goyal
a3e59697a1 ... 2022-03-03 21:24:45 +05:30
Kovid Goyal
0e2125cda3 When die-ing bring cursor back to zero column explicitly since it could happen in raw mode 2022-03-03 21:24:45 +05:30
Kovid Goyal
95da414511 Avoid one record read 2022-03-03 21:24:45 +05:30
Kovid Goyal
c0d5ace640 Prevent invalid root paths 2022-03-03 21:24:45 +05:30
Kovid Goyal
ae48407b20 untar files atomically 2022-03-03 21:24:45 +05:30
Kovid Goyal
e06bd68379 Split up ssh tests 2022-03-03 21:24:45 +05:30
Kovid Goyal
ae6665493a Implement setting of env vars 2022-03-03 21:24:44 +05:30
Kovid Goyal
c6f37afeff Add config to control shell integration per host 2022-03-03 21:24:44 +05:30
Kovid Goyal
59f656e3ca Allow controlling where on the remote computer the ssh kitten installs its data 2022-03-03 21:24:44 +05:30
Kovid Goyal
12658c4756 Make ssh data start request transmission more robust 2022-03-03 21:24:44 +05:30
Kovid Goyal
37c185462a Handle error messages form terminal during bootstrap 2022-03-03 21:24:44 +05:30
Kovid Goyal
53c8485a7a Get env conf working with tests 2022-03-03 21:24:44 +05:30
Kovid Goyal
846021296f Start work on config infrastructure for ssh kitten 2022-03-03 21:24:44 +05:30
Kovid Goyal
5c8651c7cd bootstrap script should work even when stdout is redirected 2022-03-03 21:24:44 +05:30
Kovid Goyal
ad91f5af53 ... 2022-03-03 21:24:44 +05:30
Kovid Goyal
02a68e7541 Pass the value of shell_integration in the tarfile
Allows per host overrides and also avoids needing to read kitty.conf in
the ssh kitten
2022-03-03 21:24:44 +05:30
Kovid Goyal
6e5dbc5285 Test for handling of leading data 2022-03-03 21:24:44 +05:30
Kovid Goyal
fda9415873 Use a record separator to delimit the start of data
Needed because the user could press keys and send data to the tty
before kitty can send the ssh data
2022-03-03 21:24:44 +05:30
Kovid Goyal
4054163447 DRYer 2022-03-03 21:24:44 +05:30
Kovid Goyal
e1f5ef59c8 Remove integration tests with login_shell=''since that is tested explicitly before 2022-03-03 21:24:44 +05:30
Kovid Goyal
44baeb6924 Run login_shell detection tests with all available shells 2022-03-03 21:24:44 +05:30
Kovid Goyal
c00e8b1709 Add tests for the individual login_shell detection functions 2022-03-03 21:24:44 +05:30
Kovid Goyal
3fb7ce7100 When using KITTY_DEVELOP_FROM load resources from the develop dir 2022-03-03 21:24:44 +05:30
Kovid Goyal
86b15ad693 fix detection of python when no python2 or python3 links present 2022-03-03 21:24:43 +05:30
Kovid Goyal
53c00ac631 Fix detection of login shell from passwd 2022-03-03 21:24:43 +05:30
Kovid Goyal
99d0c2d846 Get reading data working on macOS 2022-03-03 21:24:43 +05:30
Kovid Goyal
55ede897b9 faster fail 2022-03-03 21:24:43 +05:30
Kovid Goyal
a71e7d7eb1 Simplify data transmission 2022-03-03 21:24:43 +05:30
Kovid Goyal
3c28a4f723 sh on macOS doesnt have echo -n 2022-03-03 21:24:43 +05:30
Kovid Goyal
6b681df473 Include all terminfo variants in binary bundles
Needed so that they can be transmitted over ssh easily
2022-03-03 21:24:43 +05:30
Kovid Goyal
22c1ee7dc8 Cannot call the parser from callbacks that are themselves called from the parser 2022-03-03 21:24:43 +05:30
Kovid Goyal
c24e16e8cd debug function 2022-03-03 21:24:43 +05:30
Kovid Goyal
d452a5cdce ... 2022-03-03 21:24:43 +05:30
Kovid Goyal
198dd52700 Better error messages 2022-03-03 21:24:43 +05:30
Kovid Goyal
2dfea0f213 Fix a couple of conditions so they work in older dash 2022-03-03 21:24:43 +05:30
Kovid Goyal
5064b5c2b1 Apparently posh on CI is utterly broken 2022-03-03 21:24:43 +05:30
Kovid Goyal
78b553e55e Add tests for turning off shell integration in the bootstrap script 2022-03-03 21:24:43 +05:30
Kovid Goyal
8ca3a511cc Get the ssh bootstrap tests fully working 2022-03-03 21:24:43 +05:30
Kovid Goyal
bf26a3f569 Skip bash if its too old when testing bootstrap script 2022-03-03 21:24:43 +05:30
Kovid Goyal
ddb8753548 Work on enabling shell integration over ssh 2022-03-03 21:24:43 +05:30
Kovid Goyal
e73525d0a2 Start work on testing the bootstrap script 2022-03-03 21:24:43 +05:30
Kovid Goyal
f37d947dd5 more work on ssh data transmission 2022-03-03 21:24:42 +05:30
Kovid Goyal
4279f20daf Dont depend on the default bash prompt when testing 2022-03-03 21:24:42 +05:30
Kovid Goyal
2dd7c3b939 More work on ssh bootstrap 2022-03-03 21:24:42 +05:30
Kovid Goyal
cf01480ec8 An echo escape code to echo data via the TTY 2022-03-03 21:24:42 +05:30
Kovid Goyal
d8ed42ae8e Move setting of ZLE_RPROMPT_INDENT to zshrc which will hopefully prevent it being overriden 2022-03-03 19:16:44 +05:30
Kovid Goyal
297592242c Fix #4782
Presumably the failure is caused by zsh not having finished drawing the
prompt when the cursor shape is changed. So instead wait till the
expected prompt is drawn. There are probably more places where this
change needs to be made, which I cant tell since I cant reproduce.
2022-03-03 17:22:42 +05:30
Kovid Goyal
1c0a8a49fc Merge branch 'fix-nerd-fonts' of https://github.com/page-down/kitty 2022-03-03 14:41:46 +05:30
pagedown
16298e49c7 Update Unicode input kitten names 2022-03-03 16:45:07 +08:00
pagedown
abb0b95006 Add some reasonable keywords from font awesome official aliases
These glyphs can be searched by name when using Unicode input kitten.
2022-03-03 16:44:56 +08:00
pagedown
32f5ea7b63 Fix overlapping nerd fonts glyph 2022-03-03 16:29:29 +08:00
Kovid Goyal
90ed5959de version 0.24.4 2022-03-03 10:54:01 +05:30
Kovid Goyal
d999cc4143 Merge branch 'patch-1' of https://github.com/rqel/kitty 2022-03-03 08:03:25 +05:30
Kovid Goyal
6c1d8d5c63 Add instructions on where to get the NERD font 2022-03-03 08:01:36 +05:30
rqel
a316242a4b Update symbol_map unicode codepoint range for Symbols Nerd Font
Signed-off-by: rqel <rqel@pm.me>
2022-03-02 18:19:13 -08:00
Kovid Goyal
a4ae090c37 commit surface even for out of order configures 2022-03-03 07:18:04 +05:30
Kovid Goyal
abdcc64053 Merge branch 'always-commit' of https://github.com/Nefsen402/kitty 2022-03-03 07:16:50 +05:30
Alexander Orzechowski
ac407d42de wayland: always commit surface on configure 2022-03-02 14:43:49 -05:00
rqel
19c4a3f0a8 Add U+E0CA
Signed-off-by: rqel <rqel@pm.me>
2022-03-02 07:07:05 -08:00
rqel
3afd96d421 Update symbol_map
Signed-off-by: rqel <rqel@pm.me>
2022-03-02 06:37:18 -08:00
Kovid Goyal
f421666a27 Nicer error message when importing a kitten fails 2022-03-02 08:26:06 +05:30
Kovid Goyal
af6baa33be Ignore out of order surface configure events 2022-03-01 07:59:17 +05:30
Kovid Goyal
1096cbe236 Use an anonymous struct 2022-03-01 07:46:45 +05:30
Kovid Goyal
998be9b90c Merge branch 'wayland-fix-configure' of https://github.com/Nefsen402/kitty 2022-03-01 07:45:24 +05:30
Alexander Orzechowski
854cb8f27e wayland: handle resize requests after acknowledging the configure
The wayland protocol requires that clients commit a new buffer sometime
after `xdg_surface_ack_configure`.
2022-02-28 19:42:11 -05:00
Alexander Orzechowski
e359094cff wayland: move window state 2022-02-28 19:42:11 -05:00
Kovid Goyal
db57230987 Cleanup changelog 2022-02-28 20:47:08 +05:30
Kovid Goyal
4de3cecbbe Update changelog for previous PR
Fixes #4763
2022-02-28 20:21:06 +05:30
Kovid Goyal
c37a8bd3b1 Merge branch 'fix-read-scr-size' of https://github.com/page-down/kitty 2022-02-28 20:19:44 +05:30
Kovid Goyal
e103b280fd Add test for HISTFILE 2022-02-28 20:09:54 +05:30
pagedown
2235ea67e2 Fix ScreenSizeGetter 2022-02-28 22:39:19 +08:00
Kovid Goyal
1f6a4f7bd4 ... 2022-02-28 20:07:57 +05:30
Kovid Goyal
8c7ffc90f3 Fix the default bash HISTFILE changing to .sh_history instead of .bash_history
Fixes #4765
2022-02-28 20:06:48 +05:30
Kovid Goyal
29d85833f1 Merge branch 'ksi-fish' of https://github.com/page-down/kitty 2022-02-28 17:50:05 +05:30
pagedown
74c56d69ac Also fall back to check the last known working version of fish 2022-02-28 19:26:20 +08:00
Kovid Goyal
cf520646a9 Merge branch 'feat-launch-url-err' of https://github.com/page-down/kitty 2022-02-28 13:12:14 +05:30
pagedown
da5213346a show_error kitten: Use green color for closing tips 2022-02-28 15:02:16 +08:00
pagedown
bdcb98eafc Use show_error kitten to display launch URL error messages 2022-02-28 15:02:05 +08:00
Kovid Goyal
ef637cd7d3 ... 2022-02-28 11:28:42 +05:30
Kovid Goyal
dbbabd73c5 Repeat hold message on ctrl+c/d instead of completely ignoring or ending hold 2022-02-28 11:17:00 +05:30
Kovid Goyal
436ef0074a DRYer 2022-02-28 11:08:32 +05:30
Kovid Goyal
0904dec810 Fix exception on ctrl+c exit of program under --hold 2022-02-28 11:02:06 +05:30
Kovid Goyal
0aa07ead7e Bundle wayland client libraries in the binary build
The version of libwayland kitty builds against is now 1.20 and this
introduces the new wl_proxy_marshal_flags function. The usual suspect
distros have too old libwayland-client so we need to bundle.

Fixes #4760
2022-02-28 10:27:44 +05:30
273 changed files with 46700 additions and 36018 deletions

View File

@@ -31,7 +31,11 @@ def install_deps():
sys.stdout.flush() sys.stdout.flush()
if is_macos: if is_macos:
items = (x.split()[1].strip('"') for x in open('Brewfile').readlines() if x.strip().startswith('brew ')) items = (x.split()[1].strip('"') for x in open('Brewfile').readlines() if x.strip().startswith('brew '))
run('brew', 'install', 'fish', *items) openssl = 'openssl'
import ssl
if ssl.OPENSSL_VERSION_INFO[0] == 1:
openssl += '@1.1'
run('brew', 'install', 'fish', openssl, *items)
else: else:
run('sudo apt-get update') run('sudo apt-get update')
run('sudo apt-get install -y libgl1-mesa-dev libxi-dev libxrandr-dev libxinerama-dev ca-certificates' run('sudo apt-get install -y libgl1-mesa-dev libxi-dev libxrandr-dev libxinerama-dev ca-certificates'
@@ -59,7 +63,7 @@ def build_kitty():
def test_kitty(): def test_kitty():
run('./kitty/launcher/kitty +launch test.py') run('./test.py')
def package_kitty(): def package_kitty():

View File

@@ -20,15 +20,15 @@ jobs:
cc: [gcc, clang] cc: [gcc, clang]
include: include:
- python: a - python: a
pyver: 3.7 pyver: "3.8"
sanitize: 0 sanitize: 0
- python: b - python: b
pyver: 3.8 pyver: "3.9"
sanitize: 1 sanitize: 1
- python: c - python: c
pyver: 3.9 pyver: "3.10"
sanitize: 1 sanitize: 1
@@ -42,11 +42,11 @@ jobs:
steps: steps:
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@master uses: actions/checkout@v3
with: with:
fetch-depth: 10 fetch-depth: 10
- name: Set up Python ${{ matrix.pyver }} - name: Set up Python ${{ matrix.pyver }}
uses: actions/setup-python@master uses: actions/setup-python@v3
with: with:
python-version: ${{ matrix.pyver }} python-version: ${{ matrix.pyver }}
@@ -63,17 +63,17 @@ jobs:
CFLAGS: -funsigned-char CFLAGS: -funsigned-char
steps: steps:
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@master uses: actions/checkout@v3
with: with:
fetch-depth: 10 fetch-depth: 0 # needed for :commit: docs role
- name: Test for trailing whitespace - name: Test for trailing whitespace
run: if grep -Inr '\s$' kitty kitty_tests kittens docs *.py *.asciidoc *.rst .gitattributes .gitignore; then echo Trailing whitespace found, aborting.; exit 1; fi run: if grep -Inr '\s$' kitty kitty_tests kittens docs *.py *.asciidoc *.rst .gitattributes .gitignore; then echo Trailing whitespace found, aborting.; exit 1; fi
- name: Set up Python - name: Set up Python
uses: actions/setup-python@master uses: actions/setup-python@v3
with: with:
python-version: 3.8 python-version: 3.9
- name: Install build-only deps - name: Install build-only deps
run: pip install -r docs/requirements.txt flake8 mypy types-requests types-docutils run: pip install -r docs/requirements.txt flake8 mypy types-requests types-docutils
@@ -84,8 +84,11 @@ jobs:
- name: Build kitty package - name: Build kitty package
run: python .github/workflows/ci.py package run: python .github/workflows/ci.py package
- name: Build kitty
run: python setup.py build --debug
- name: Run mypy - name: Run mypy
run: python test.py mypy run: ./test.py mypy
- name: Build man page - name: Build man page
run: make FAIL_WARN=1 man run: make FAIL_WARN=1 man
@@ -120,7 +123,7 @@ jobs:
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@master uses: actions/checkout@master
with: with:
fetch-depth: 10 fetch-depth: 0 # needed for :commit: docs role
- name: Build kitty - name: Build kitty
run: python3 .github/workflows/ci.py build run: python3 .github/workflows/ci.py build
@@ -128,5 +131,11 @@ jobs:
- name: Test kitty - name: Test kitty
run: python3 .github/workflows/ci.py test run: python3 .github/workflows/ci.py test
- name: Install deps for docs
run: python3 -m pip install -r docs/requirements.txt
- name: Builds docs
run: make FAIL_WARN=1 docs
- name: Build kitty package - name: Build kitty package
run: python3 .github/workflows/ci.py package run: python3 .github/workflows/ci.py package

View File

@@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
# We must fetch at least the immediate parents so that if this is # We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head. # a pull request then we can checkout the head.

4
.gitignore vendored
View File

@@ -2,6 +2,8 @@
*.pyc *.pyc
*.pyo *.pyo
*_stub.pyi *_stub.pyi
*_generated.go
*_generated.h
/.dmypy.json /.dmypy.json
/tags /tags
/build/ /build/
@@ -10,7 +12,7 @@
/compile_commands.json /compile_commands.json
/link_commands.json /link_commands.json
/glad/out/ /glad/out/
/kitty/launcher/ /kitty/launcher/kitty*
/*.dSYM/ /*.dSYM/
__pycache__/ __pycache__/
/glfw/wayland-*-client-protocol.[ch] /glfw/wayland-*-client-protocol.[ch]

View File

@@ -35,6 +35,12 @@ profile:
app: app:
python3 setup.py kitty.app $(VVAL) python3 setup.py kitty.app $(VVAL)
linux-package: FORCE
rm -rf linux-package
python3 setup.py linux-package
FORCE:
man: man:
$(MAKE) -C docs man $(MAKE) -C docs man

View File

@@ -1,184 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
import os
import sys
from typing import List
def icat(args: List[str]) -> None:
from kittens.runner import run_kitten as rk
sys.argv = args
rk('icat')
def list_fonts(args: List[str]) -> None:
from kitty.fonts.list import main as list_main
list_main(args)
def remote_control(args: List[str]) -> None:
from kitty.remote_control import main as rc_main
rc_main(args)
def runpy(args: List[str]) -> None:
if len(args) < 2:
raise SystemExit('Usage: kitty +runpy "some python code"')
sys.argv = ['kitty'] + args[2:]
exec(args[1])
def hold(args: List[str]) -> None:
import subprocess
try:
ret = subprocess.Popen(args[1:]).wait()
except KeyboardInterrupt:
pass
except FileNotFoundError:
print(f'Could not find {args[1]!r} to execute', file=sys.stderr)
except Exception as e:
print(e, file=sys.stderr)
from kitty.utils import hold_till_enter
hold_till_enter()
raise SystemExit(ret)
def complete(args: List[str]) -> None:
from kitty.complete import main as complete_main
complete_main(args[1:], entry_points, namespaced_entry_points)
def open_urls(args: List[str]) -> None:
setattr(sys, 'cmdline_args_for_open', True)
sys.argv = ['kitty'] + args[1:]
from kitty.main import main as kitty_main
kitty_main()
def launch(args: List[str]) -> None:
import runpy
sys.argv = args[1:]
try:
exe = args[1]
except IndexError:
raise SystemExit(
'usage: kitty +launch script.py [arguments to be passed to script.py ...]\n\n'
'script.py will be run with full access to kitty code. If script.py is '
'prefixed with a : it will be searched for in PATH'
)
if exe.startswith(':'):
import shutil
q = shutil.which(exe[1:])
if not q:
raise SystemExit(f'{exe[1:]} not found in PATH')
exe = q
if not os.path.exists(exe):
raise SystemExit(f'{exe} does not exist')
runpy.run_path(exe, run_name='__main__')
def shebang(args: List[str]) -> None:
script_path = args[1]
cmd = args[2:]
if cmd == ['__ext__']:
cmd = [os.path.splitext(script_path)[1][1:].lower()]
try:
f = open(script_path)
except FileNotFoundError:
raise SystemExit(f'The file {script_path} does not exist')
with f:
if f.read(2) == '#!':
line = f.readline().strip()
_plat = sys.platform.lower()
is_macos: bool = 'darwin' in _plat
if is_macos:
cmd = line.split(' ')
else:
cmd = line.split(' ', maxsplit=1)
os.execvp(cmd[0], cmd + [script_path])
def run_kitten(args: List[str]) -> None:
try:
kitten = args[1]
except IndexError:
from kittens.runner import list_kittens
list_kittens()
raise SystemExit(1)
sys.argv = args[1:]
from kittens.runner import run_kitten as rk
rk(kitten)
def edit_config_file(args: List[str]) -> None:
from kitty.cli import create_default_opts
from kitty.fast_data_types import set_options
from kitty.utils import edit_config_file as f
set_options(create_default_opts())
f()
def namespaced(args: List[str]) -> None:
try:
func = namespaced_entry_points[args[1]]
except KeyError:
pass
else:
func(args[1:])
return
raise SystemExit(f'{args[1]} is not a known entry point. Choices are: ' + ', '.join(namespaced_entry_points))
entry_points = {
# These two are here for backwards compat
'icat': icat,
'list-fonts': list_fonts,
'@': remote_control,
'+': namespaced,
}
namespaced_entry_points = {k: v for k, v in entry_points.items() if k[0] not in '+@'}
namespaced_entry_points['hold'] = hold
namespaced_entry_points['complete'] = complete
namespaced_entry_points['runpy'] = runpy
namespaced_entry_points['launch'] = launch
namespaced_entry_points['open'] = open_urls
namespaced_entry_points['kitten'] = run_kitten
namespaced_entry_points['edit-config'] = edit_config_file
namespaced_entry_points['shebang'] = shebang
def setup_openssl_environment() -> None:
# Use our bundled CA certificates instead of the system ones, since
# many systems come with no certificates in a useable form or have various
# locations for the certificates.
d = os.path.dirname
ext_dir: str = getattr(sys, 'kitty_extensions_dir')
if 'darwin' in sys.platform.lower():
cert_file = os.path.join(d(d(d(ext_dir))), 'cacert.pem')
else:
cert_file = os.path.join(d(ext_dir), 'cacert.pem')
os.environ['SSL_CERT_FILE'] = cert_file
setattr(sys, 'kitty_ssl_env_var', 'SSL_CERT_FILE')
def main() -> None:
if getattr(sys, 'frozen', False) and getattr(sys, 'kitty_extensions_dir', ''):
setup_openssl_environment()
first_arg = '' if len(sys.argv) < 2 else sys.argv[1]
func = entry_points.get(first_arg)
if func is None:
if first_arg.startswith('@'):
remote_control(['@', first_arg[1:]] + sys.argv[2:])
elif first_arg.startswith('+'):
namespaced(['+', first_arg[1:]] + sys.argv[2:])
else:
from kitty.main import main as kitty_main
kitty_main()
else:
func(sys.argv[1:])
if __name__ == '__main__': if __name__ == '__main__':
from kitty.entry_points import main
main() main()

View File

@@ -4,7 +4,6 @@
import glob import glob
import os import os
import re
import shutil import shutil
import subprocess import subprocess
import sys import sys
@@ -13,11 +12,10 @@ import tempfile
def compile_terminfo(base): def compile_terminfo(base):
with tempfile.TemporaryDirectory() as tdir: with tempfile.TemporaryDirectory() as tdir:
proc = subprocess.run(['tic', '-x', f'-o{tdir}', 'terminfo/kitty.terminfo'], check=True, stderr=subprocess.PIPE) proc = subprocess.run(['tic', '-x', f'-o{tdir}', 'terminfo/kitty.terminfo'], capture_output=True)
regex = '^"terminfo/kitty.terminfo", line [0-9]+, col [0-9]+, terminal \'xterm-kitty\': older tic versions may treat the description field as an alias$' if proc.returncode != 0:
for error in proc.stderr.decode('utf-8').splitlines(): sys.stderr.buffer.write(proc.stderr)
if not re.match(regex, error): raise SystemExit(proc.returncode)
print(error, file=sys.stderr)
tfiles = glob.glob(os.path.join(tdir, '*', 'xterm-kitty')) tfiles = glob.glob(os.path.join(tdir, '*', 'xterm-kitty'))
if not tfiles: if not tfiles:
raise SystemExit('tic failed to output the compiled kitty terminfo file') raise SystemExit('tic failed to output the compiled kitty terminfo file')
@@ -40,6 +38,13 @@ def generate_terminfo():
with open('terminfo/kitty.terminfo', 'w') as f: with open('terminfo/kitty.terminfo', 'w') as f:
f.write(generate_terminfo()) f.write(generate_terminfo())
proc = subprocess.run(['tic', '-CrT0', 'terminfo/kitty.terminfo'], capture_output=True)
if proc.returncode != 0:
sys.stderr.buffer.write(proc.stderr)
raise SystemExit(proc.returncode)
tcap = proc.stdout.decode('utf-8').splitlines()[-1]
with open('terminfo/kitty.termcap', 'w') as f:
f.write(tcap)
compile_terminfo(os.path.join(base, 'terminfo')) compile_terminfo(os.path.join(base, 'terminfo'))

View File

@@ -35,7 +35,11 @@ def binary_includes():
'expat', 'sqlite3', 'ffi', 'z', 'lzma', 'png16', 'lcms2', 'crypt', 'expat', 'sqlite3', 'ffi', 'z', 'lzma', 'png16', 'lcms2', 'crypt',
'iconv', 'pcre', 'graphite2', 'glib-2.0', 'freetype', 'rsync', 'iconv', 'pcre', 'graphite2', 'glib-2.0', 'freetype', 'rsync',
'harfbuzz', 'xkbcommon', 'xkbcommon-x11', 'harfbuzz', 'xkbcommon', 'xkbcommon-x11',
'ncursesw', 'readline', 'brotlicommon', 'brotlienc', 'brotlidec' # fontconfig is not bundled because in typical brain dead Linux
# distro fashion, different distros use different default config
# paths for fontconfig.
'ncursesw', 'readline', 'brotlicommon', 'brotlienc', 'brotlidec',
'wayland-client', 'wayland-cursor',
))) + ( ))) + (
get_dll_path('bz2', 2), get_dll_path('ssl', 2), get_dll_path('crypto', 2), get_dll_path('bz2', 2), get_dll_path('ssl', 2), get_dll_path('crypto', 2),
get_dll_path(f'python{py_ver}', 2), get_dll_path(f'python{py_ver}', 2),
@@ -139,6 +143,7 @@ def copy_python(env):
iv['sanitize_source_folder'](os.path.join(env.py_dir, x)) iv['sanitize_source_folder'](os.path.join(env.py_dir, x))
py_compile(env.py_dir) py_compile(env.py_dir)
freeze_python(env.py_dir, pdir, env.obj_dir, ext_map, develop_mode_env_var='KITTY_DEVELOP_FROM', remove_pyc_files=True) freeze_python(env.py_dir, pdir, env.obj_dir, ext_map, develop_mode_env_var='KITTY_DEVELOP_FROM', remove_pyc_files=True)
shutil.rmtree(env.py_dir)
def build_launcher(env): def build_launcher(env):

View File

@@ -369,6 +369,7 @@ class Freeze(object):
iv['sanitize_source_folder'](join(self.python_stdlib, x)) iv['sanitize_source_folder'](join(self.python_stdlib, x))
self.compile_py_modules() self.compile_py_modules()
freeze_python(self.python_stdlib, pdir, self.obj_dir, ext_map, develop_mode_env_var='KITTY_DEVELOP_FROM', remove_pyc_files=True) freeze_python(self.python_stdlib, pdir, self.obj_dir, ext_map, develop_mode_env_var='KITTY_DEVELOP_FROM', remove_pyc_files=True)
shutil.rmtree(self.python_stdlib)
iv['build_frozen_launcher']([path_to_freeze_dir(), self.obj_dir]) iv['build_frozen_launcher']([path_to_freeze_dir(), self.obj_dir])
os.rename(join(dirname(self.contents_dir), 'bin', 'kitty'), join(self.contents_dir, 'MacOS', 'kitty')) os.rename(join(dirname(self.contents_dir), 'bin', 'kitty'), join(self.contents_dir, 'MacOS', 'kitty'))
shutil.rmtree(join(dirname(self.contents_dir), 'bin')) shutil.rmtree(join(dirname(self.contents_dir), 'bin'))

View File

@@ -4,18 +4,14 @@
{{ super() }} {{ super() }}
{%- if analytics_id %} {%- if analytics_id %}
<script type="text/javascript"> <!-- Global site tag (gtag.js) - Google Analytics -->
var _gaq = _gaq || []; <script async src="https://www.googletagmanager.com/gtag/js?id={{ analytics_id }}"></script>
_gaq.push(['_setAccount', '{{ analytics_id }}']); <script>
_gaq.push(['_setDomainName', 'none']); window.dataLayer = window.dataLayer || [];
_gaq.push(['_setAllowLinker', true]); function gtag(){dataLayer.push(arguments);}
_gaq.push(['_trackPageview']); gtag('js', new Date());
(function() { gtag('config', '{{ analytics_id }}');
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script> </script>
{% endif -%} {% endif -%}

View File

@@ -1,12 +1,13 @@
Tabs and Windows Tabs and Windows
------------------- -------------------
|kitty| is capable of running multiple programs organized into tabs and |kitty| is capable of running multiple programs organized into tabs and windows.
windows. The top level of organization is the *Tab*. Each tab consists The top level of organization is the :term:`OS window <os_window>`. Each OS
of one or more *windows*. The windows can be arranged in multiple window consists of one or more :term:`tabs <tab>`. Each tab consists of one or more
different layouts, like windows are organized in a tiling window :term:`kitty windows <window>`. The kitty windows can be arranged in multiple
manager. The keyboard controls (which are all customizable) for tabs and different :term:`layouts <layout>`, like windows are organized in a tiling
windows are: window manager. The keyboard controls (which are :ref:`all customizable
<conf-kitty-shortcuts>`) for tabs and windows are:
Scrolling Scrolling
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
@@ -64,8 +65,9 @@ Focus specific window :sc:`first_window`, :sc:`second_window` ... :sc:`ten
(clockwise from the top-left) (clockwise from the top-left)
======================== ======================= ======================== =======================
Additionally, you can define shortcuts in :file:`kitty.conf` to focus neighboring Additionally, you can define shortcuts in :file:`kitty.conf` to focus
windows and move windows around (similar to window movement in vim):: neighboring windows and move windows around (similar to window movement in
:program:`vim`)::
map ctrl+left neighboring_window left map ctrl+left neighboring_window left
map shift+left move_window right map shift+left move_window right
@@ -77,20 +79,20 @@ You can also define a shortcut to switch to the previously active window::
map ctrl+p nth_window -1 map ctrl+p nth_window -1
``nth_window`` will focus the nth window for positive numbers (starting from :ac:`nth_window` will focus the nth window for positive numbers (starting from
zero) and the previously active windows for negative numbers. zero) and the previously active windows for negative numbers.
To switch to the nth OS window, you can define ``nth_os_window``. Only positive To switch to the nth OS window, you can define :ac:`nth_os_window`. Only
numbers are accepted, starting from one. positive numbers are accepted, starting from one.
.. _detach_window: .. _detach_window:
You can define shortcuts to detach the current window and You can define shortcuts to detach the current window and move it to another tab
move it to another tab or another OS window:: or another OS window::
# moves the window into a new OS window # moves the window into a new OS window
map ctrl+f2 detach_window map ctrl+f2 detach_window
# moves the window into a new Tab # moves the window into a new tab
map ctrl+f3 detach_window new-tab map ctrl+f3 detach_window new-tab
# moves the window into the previously active tab # moves the window into the previously active tab
map ctrl+f3 detach_window tab-prev map ctrl+f3 detach_window tab-prev
@@ -106,8 +108,8 @@ Similarly, you can detach the current tab, with::
# asks which OS Window to move the tab into # asks which OS Window to move the tab into
map ctrl+f4 detach_tab ask map ctrl+f4 detach_tab ask
Finally, you can define a shortcut to close all windows in a tab other than Finally, you can define a shortcut to close all windows in a tab other than the
the currently active window:: currently active window::
map f9 close_other_windows_in_tab map f9 close_other_windows_in_tab
@@ -121,21 +123,22 @@ The full list of actions that can be mapped to key presses is available
================================== ======================= ================================== =======================
Action Shortcut Action Shortcut
================================== ======================= ================================== =======================
Show this help :sc:`show_kitty_doc`
Copy to clipboard :sc:`copy_to_clipboard` (also :kbd:`⌘+c` on macOS) Copy to clipboard :sc:`copy_to_clipboard` (also :kbd:`⌘+c` on macOS)
Paste from clipboard :sc:`paste_from_clipboard` (also :kbd:`⌘+v` on macOS) Paste from clipboard :sc:`paste_from_clipboard` (also :kbd:`⌘+v` on macOS)
Paste from selection :sc:`paste_from_selection` Paste from selection :sc:`paste_from_selection`
Pass selection to program :sc:`pass_selection_to_program`
Increase font size :sc:`increase_font_size` (also :kbd:`⌘++` on macOS) Increase font size :sc:`increase_font_size` (also :kbd:`⌘++` on macOS)
Decrease font size :sc:`decrease_font_size` (also :kbd:`⌘+-` on macOS) Decrease font size :sc:`decrease_font_size` (also :kbd:`⌘+-` on macOS)
Restore font size :sc:`reset_font_size` (also :kbd:`⌘+0` on macOS) Restore font size :sc:`reset_font_size` (also :kbd:`⌘+0` on macOS)
Toggle fullscreen :sc:`toggle_fullscreen` (also :kbd:`⌃+⌘+f` on macOS) Toggle fullscreen :sc:`toggle_fullscreen` (also :kbd:`⌃+⌘+f` on macOS)
Toggle maximized :sc:`toggle_maximized` Toggle maximized :sc:`toggle_maximized`
Input unicode character :sc:`input_unicode_character` (also :kbd:`⌃+⌘+space` on macOS) Input Unicode character :sc:`input_unicode_character` (also :kbd:`⌃+⌘+space` on macOS)
Open URL in web browser :sc:`open_url` Open URL in web browser :sc:`open_url`
Reset the terminal :sc:`reset_terminal` (also :kbd:`⌥+⌘+r` on macOS) Reset the terminal :sc:`reset_terminal` (also :kbd:`⌥+⌘+r` on macOS)
Edit :file:`kitty.conf` :sc:`edit_config_file` (also :kbd:`⌘+,` on macOS)
Reload :file:`kitty.conf` :sc:`reload_config_file` (also :kbd:`⌃+⌘+,` on macOS) Reload :file:`kitty.conf` :sc:`reload_config_file` (also :kbd:`⌃+⌘+,` on macOS)
Debug :file:`kitty.conf` :sc:`debug_config` (also :kbd:`⌥+⌘+,` on macOS) Debug :file:`kitty.conf` :sc:`debug_config` (also :kbd:`⌥+⌘+,` on macOS)
Pass current selection to program :sc:`pass_selection_to_program`
Edit |kitty| config file :sc:`edit_config_file` (also :kbd:`⌘+,` on macOS)
Open a |kitty| shell :sc:`kitty_shell` Open a |kitty| shell :sc:`kitty_shell`
Increase background opacity :sc:`increase_background_opacity` Increase background opacity :sc:`increase_background_opacity`
Decrease background opacity :sc:`decrease_background_opacity` Decrease background opacity :sc:`decrease_background_opacity`

View File

@@ -4,17 +4,14 @@ Install kitty
Binary install Binary install
---------------- ----------------
.. |ins| replace:: curl -L :literal:`https://sw.kovidgoyal.net/kitty/installer.sh` | sh /dev/stdin
.. highlight:: sh .. highlight:: sh
You can install pre-built binaries of |kitty| if you are on macOS or Linux using You can install pre-built binaries of |kitty| if you are on macOS or Linux using
the following simple command: the following simple command:
.. parsed-literal:: .. code-block:: sh
:class: pre
|ins| _kitty_install_cmd
The binaries will be installed in the standard location for your OS, The binaries will be installed in the standard location for your OS,
@@ -24,7 +21,7 @@ simply re-run the command.
.. warning:: .. warning::
**Do not** copy the kitty binary out of the installation folder. If you want **Do not** copy the kitty binary out of the installation folder. If you want
to add it to your ``PATH`` create a symlink in :file:`~/.local/bin` or to add it to your :envvar:`PATH`, create a symlink in :file:`~/.local/bin` or
:file:`/usr/bin` or wherever. :file:`/usr/bin` or wherever.
@@ -33,11 +30,12 @@ Manually installing
If something goes wrong or you simply do not want to run the installer, you can If something goes wrong or you simply do not want to run the installer, you can
manually download and install |kitty| from the `GitHub releases page manually download and install |kitty| from the `GitHub releases page
<https://github.com/kovidgoyal/kitty/releases>`_. If you are on macOS, download <https://github.com/kovidgoyal/kitty/releases>`__. If you are on macOS, download
the :file:`.dmg` and install as normal. If you are on Linux, download the tarball the :file:`.dmg` and install as normal. If you are on Linux, download the
and extract it into a directory. The |kitty| executable will be in the tarball and extract it into a directory. The |kitty| executable will be in the
:file:`bin` sub-directory. :file:`bin` sub-directory.
Desktop integration on Linux Desktop integration on Linux
-------------------------------- --------------------------------
@@ -49,19 +47,30 @@ particular desktop, but it should work for most major desktop environments.
.. code-block:: sh .. code-block:: sh
# Create a symbolic link to add kitty to PATH (assuming ~/.local/bin is in # Create a symbolic link to add kitty to PATH (assuming ~/.local/bin is in
# your PATH) # your system-wide PATH)
ln -s ~/.local/kitty.app/bin/kitty ~/.local/bin/ ln -s ~/.local/kitty.app/bin/kitty ~/.local/bin/
# Place the kitty.desktop file somewhere it can be found by the OS # Place the kitty.desktop file somewhere it can be found by the OS
cp ~/.local/kitty.app/share/applications/kitty.desktop ~/.local/share/applications/ cp ~/.local/kitty.app/share/applications/kitty.desktop ~/.local/share/applications/
# If you want to open text files and images in kitty via your file manager also add the kitty-open.desktop file # If you want to open text files and images in kitty via your file manager also add the kitty-open.desktop file
cp ~/.local/kitty.app/share/applications/kitty-open.desktop ~/.local/share/applications/ cp ~/.local/kitty.app/share/applications/kitty-open.desktop ~/.local/share/applications/
# Update the path to the kitty icon in the kitty.desktop file(s) # Update the paths to the kitty and its icon in the kitty.desktop file(s)
sed -i "s|Icon=kitty|Icon=/home/$USER/.local/kitty.app/share/icons/hicolor/256x256/apps/kitty.png|g" ~/.local/share/applications/kitty*.desktop sed -i "s|Icon=kitty|Icon=/home/$USER/.local/kitty.app/share/icons/hicolor/256x256/apps/kitty.png|g" ~/.local/share/applications/kitty*.desktop
sed -i "s|Exec=kitty|Exec=/home/$USER/.local/kitty.app/bin/kitty|g" ~/.local/share/applications/kitty*.desktop
.. note:: .. note::
If you use the venerable `stow <https://www.gnu.org/software/stow/>`_ In :file:`kitty-open.desktop`, kitty is registered to handle some supported
MIME types. This will cause kitty to take precedence on some systems where
the default apps are not explicitly set. For example, you expect to use
other GUI file managers to open dir paths when using commands such as
:program:`xdg-open`, you should configure the default opener for the MIME
type ``inode/directory``::
xdg-mime default org.kde.dolphin.desktop inode/directory
.. note::
If you use the venerable `stow <https://www.gnu.org/software/stow/>`__
command to manage your manual installations, the following takes care of the command to manage your manual installations, the following takes care of the
above for you (use with :file:`dest=~/.local/stow`):: above for you (use with :code:`dest=~/.local/stow`)::
cd ~/.local/stow cd ~/.local/stow
stow -v kitty.app stow -v kitty.app
@@ -74,44 +83,39 @@ Customizing the installation
* You can install the latest nightly kitty build with ``installer``: * You can install the latest nightly kitty build with ``installer``:
.. parsed-literal:: .. code-block:: sh
:class: pre
|ins| \\ _kitty_install_cmd \\
installer=nightly installer=nightly
If you want to install it in parallel to the released kitty specify a If you want to install it in parallel to the released kitty specify a
different install locations with ``dest``: different install locations with ``dest``:
.. parsed-literal:: .. code-block:: sh
:class: pre
|ins| \\ _kitty_install_cmd \\
installer=nightly dest=/some/other/location installer=nightly dest=/some/other/location
* You can specify a different install location, with ``dest``: * You can specify a different install location, with ``dest``:
.. parsed-literal:: .. code-block:: sh
:class: pre
|ins| \\ _kitty_install_cmd \\
dest=/some/other/location dest=/some/other/location
* You can tell the installer not to launch |kitty| after installing it with * You can tell the installer not to launch |kitty| after installing it with
``launch=n``: ``launch=n``:
.. parsed-literal:: .. code-block:: sh
:class: pre
|ins| \\ _kitty_install_cmd \\
launch=n launch=n
* You can use a previously downloaded dmg/tarball, with ``installer``: * You can use a previously downloaded dmg/tarball, with ``installer``:
.. parsed-literal:: .. code-block:: sh
:class: pre
|ins| \\ _kitty_install_cmd \\
installer=/path/to/dmg or tarball installer=/path/to/dmg or tarball

View File

@@ -7,21 +7,21 @@ Build from source
.. highlight:: sh .. highlight:: sh
|kitty| is designed to run from source, for easy hack-ability. Make sure |kitty| is designed to run from source, for easy hack-ability. Make sure the
the following dependencies are installed first. following dependencies are installed first.
.. note:: .. note::
If you just want to test the latest changes to kitty you dont need to build If you just want to test the latest changes to kitty, you don't need to build
from source. Instead install the :ref:`latest nightly build <nightly>`. from source. Instead install the :ref:`latest nightly build <nightly>`.
.. note:: .. note::
If you are making small changes only to the python parts of kitty, there is no need to If you are making small changes only to the Python parts of kitty, there is
build kitty at all, instead, assuming you have installed the official kitty no need to build kitty at all, instead, assuming you have installed the
binaries, you can simply set the KITTY_DEVELOP_FROM enviroment variable to official kitty binaries, you can simply set the :envvar:`KITTY_DEVELOP_FROM`
point to the directory into which you have checked out the kitty source enviroment variable to point to the directory into which you have checked out
code. kitty will then load its python code from there. You should use a the kitty source code. kitty will then load its Python code from there. You
version of the source that matches the binary version as closely as should use a version of the source that matches the binary version as closely
possible, since the two are tightly coupled. as possible, since the two are tightly coupled.
Dependencies Dependencies
@@ -29,12 +29,13 @@ Dependencies
Run-time dependencies: Run-time dependencies:
* ``python`` >= 3.6 * ``python`` >= 3.8
* ``harfbuzz`` >= 2.2.0 * ``harfbuzz`` >= 2.2.0
* ``zlib`` * ``zlib``
* ``libpng`` * ``libpng``
* ``liblcms2`` * ``liblcms2``
* ``librsync`` * ``librsync``
* ``openssl``
* ``freetype`` (not needed on macOS) * ``freetype`` (not needed on macOS)
* ``fontconfig`` (not needed on macOS) * ``fontconfig`` (not needed on macOS)
* ``libcanberra`` (not needed on macOS) * ``libcanberra`` (not needed on macOS)
@@ -46,7 +47,9 @@ Build-time dependencies:
* ``gcc`` or ``clang`` * ``gcc`` or ``clang``
* ``pkg-config`` * ``pkg-config``
* 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: * 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:
- ``libdbus-1-dev`` - ``libdbus-1-dev``
- ``libxcursor-dev`` - ``libxcursor-dev``
@@ -77,9 +80,12 @@ You can run |kitty|, as::
./kitty/launcher/kitty ./kitty/launcher/kitty
If that works, you can create a symlink to the launcher in :file:`~/bin` or If that works, you can create a symlink to the launcher in :file:`~/bin` or some
some other directory on your PATH so that you can run |kitty| using other directory on your PATH so that you can run |kitty| using just ``kitty``.
just ``kitty``.
To have the kitty documentation available locally, run::
python3 -m pip install -r docs/requirements.txt && make docs
Building kitty.app on macOS from source Building kitty.app on macOS from source
@@ -87,28 +93,30 @@ Building kitty.app on macOS from source
Run:: Run::
python3 -m pip install -r docs/requirements.txt && make docs
make app make app
Building the docs needs to be done only once.
This :file:`kitty.app` unlike the released one does not include its own copy of This :file:`kitty.app` unlike the released one does not include its own copy of
python and the other dependencies. So if you ever un-install/upgrade those dependencies Python and the other dependencies. So if you ever un-install/upgrade those
you might have to rebuild the app. dependencies you might have to rebuild the app.
.. note:: .. note::
The released :file:`kitty.dmg` includes all dependencies, unlike the The released :file:`kitty.dmg` includes all dependencies, unlike the
:file:`kitty.app` built above and is built automatically by using the :file:`kitty.app` built above and is built automatically by using the
`bypy framework <https://github.com/kovidgoyal/bypy>`_ however, that is `bypy framework <https://github.com/kovidgoyal/bypy>`__ however, that is
designed to run on Linux and is not for the faint of heart. designed to run on Linux and is not for the faint of heart.
.. note:: .. note::
Apple disallows certain functionality, such as notifications for unsigned applications. Apple disallows certain functionality, such as notifications for unsigned
If you need this functionality, you can try signing the built kitty.app with applications. If you need this functionality, you can try signing the built
a self signed certificate, see for example, `here :file:`kitty.app` with a self signed certificate, see for example, `here
<https://stackoverflow.com/questions/27474751/how-can-i-codesign-an-app-without-being-in-the-mac-developer-program/27474942>`_. <https://stackoverflow.com/questions/27474751/how-can-i-codesign-an-app-without-being-in-the-mac-developer-program/27474942>`__.
.. note:: .. note::
If you are facing issues with ``linker`` while building, If you are facing issues with ``linker`` while building, try with a ``brew``
try with a ``brew`` installed python instead, see :iss:`289` installed Python instead, see :iss:`289` for more discussion.
for more discussion.
Build and run from source with Nix Build and run from source with Nix
@@ -116,87 +124,78 @@ Build and run from source with Nix
On NixOS or any other Linux or macOS system with the Nix package manager On NixOS or any other Linux or macOS system with the Nix package manager
installed, execute `nix-shell installed, execute `nix-shell
<https://nixos.org/guides/nix-pills/developing-with-nix-shell.html>`_ to create <https://nixos.org/guides/nix-pills/developing-with-nix-shell.html>`__ to create
the correct environment to build kitty or use ``nix-shell --pure`` instead to the correct environment to build kitty or use ``nix-shell --pure`` instead to
eliminate most of the influence of the outside system, e.g. globally installed eliminate most of the influence of the outside system, e.g. globally installed
packages. ``nix-shell`` will automatically fetch all required dependencies and packages. ``nix-shell`` will automatically fetch all required dependencies and
make them available in the newly spawned shell. make them available in the newly spawned shell.
Then proceed with ``make`` or ``make app`` according to the platform specific instructions above. Then proceed with ``make`` or ``make app`` according to the platform specific
instructions above.
Debug builds
--------------
A basic debug build can be done with::
make debug
This includes debug info in the binary for better traces. To build with address
sanitizer, use::
make asan
Which will result in a debug binary that uses the address sanitizer as well.
.. _packagers: .. _packagers:
Notes for Linux/macOS packagers Notes for Linux/macOS packagers
---------------------------------- ----------------------------------
The released |kitty| source code is available as a `tarball`_ from The released |kitty| source code is available as a `tarball`_ from
`the GitHub releases page <https://github.com/kovidgoyal/kitty/releases>`_. `the GitHub releases page <https://github.com/kovidgoyal/kitty/releases>`__.
While |kitty| does use python, it is not a traditional python package, so please While |kitty| does use Python, it is not a traditional Python package, so please
do not install it in site-packages. do not install it in site-packages.
Instead run:: Instead run::
python3 setup.py linux-package python3 setup.py linux-package
This will install |kitty| into the directory :file:`linux-package`. You can run |kitty| This will install |kitty| into the directory :file:`linux-package`. You can run
with :file:`linux-package/bin/kitty`. All the files needed to run kitty will be in |kitty| with :file:`linux-package/bin/kitty`. All the files needed to run kitty
:file:`linux-package/lib/kitty`. The terminfo file will be installed into will be in :file:`linux-package/lib/kitty`. The terminfo file will be installed
:file:`linux-package/share/terminfo`. Simply copy these files into :file:`/usr` to install into :file:`linux-package/share/terminfo`. Simply copy these files into
|kitty|. In other words, :file:`linux-package` is the staging area into which |kitty| is :file:`/usr` to install |kitty|. In other words, :file:`linux-package` is the
installed. You can choose a different staging area, by passing the ``--prefix`` staging area into which |kitty| is installed. You can choose a different staging
argument to :file:`setup.py`. area, by passing the ``--prefix`` argument to :file:`setup.py`.
You should probably split |kitty| into three packages: You should probably split |kitty| into three packages:
:code:`kitty-terminfo` :code:`kitty-terminfo`
installs the terminfo file Installs the terminfo file
:code:`kitty-shell-integration` :code:`kitty-shell-integration`
installs the shell integration scripts (the contents of the Installs the shell integration scripts (the contents of the
shell-integration directory in the kitty source code, probably to shell-integration directory in the kitty source code), probably to
:file:`/usr/share/kitty/shell-integration` :file:`/usr/share/kitty/shell-integration`
:code:`kitty` :code:`kitty`
installs the main program Installs the main program
This allows users to install the terminfo and shell integration files on This allows users to install the terminfo and shell integration files on
servers into which they ssh, without needing to install all of |kitty|. servers into which they ssh, without needing to install all of |kitty|. The
shell integration files **must** still be present in
:file:`lib/kitty/shell-integration` when installing the kitty main package as
the kitty program expects to find them there.
.. note:: .. note::
You need a couple of extra dependencies to build linux-package. You need a couple of extra dependencies to build linux-package. :file:`tic`
:file:`tic` to compile terminfo files, usually found in the to compile terminfo files, usually found in the development package of
development package of :file:`ncurses`. Also, if you are building from :file:`ncurses`. Also, if you are building from a git checkout instead of the
a git checkout instead of the released source code tarball, you will released source code tarball, you will need to install the dependencies from
need to install the dependencies from ``docs/requirements.txt`` to :file:`docs/requirements.txt` to build the kitty documentation. They can be
build the kitty documentation. They can be installed most easily with installed most easily with ``python -m pip -r docs/requirements.txt``.
``python -m pip -r docs/requirements.txt``.
This applies to creating packages for |kitty| for macOS package managers such as This applies to creating packages for |kitty| for macOS package managers such as
Homebrew or MacPorts as well. Homebrew or MacPorts as well.
Changing defaults for packages
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|kitty| has its defaults chosen to be suitable for a standalone distributable.
If you are packaging it a few of these might need to be changed.
update-checking
|kitty| has its own update check mechanism, if you would like to turn
it off for your package, use::
./setup.py linux-package --update-check-interval=0
shell-integration
|kitty| by default injects its :ref:`shell_integration` code into the user's
shell using environment variables. For a package, it might make more sense
to distribute the shell integration scripts into the system-wide shell
vendor locations. The shell integration files are found in the
:file:`shell-integration` directory. Copy them to the system wide shell
vendor locations for each shell, and use::
./setup.py linux-package --shell-integration=enabled\ no-rc
This will prevent kitty from modifying the user's shell environment to load
the integration scripts.

View File

@@ -9,23 +9,15 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
Recent major new features Recent major new features
--------------------------- ---------------------------
.. only:: dirhtml Truly convenient SSH
~~~~~~~~~~~~~~~~~~~~~~~~
A demo video The :doc:`ssh kitten <kittens/ssh>` is redesigned with powerful new features:
~~~~~~~~~~~~~~~~
A new video showcasing some of kitty's many productivity enhancing features. * Automatic :ref:`shell_integration` on remote machines
* Easily :ref:`clone local shell/editor config <real_world_ssh_kitten_config>` on remote machines
.. raw:: html * Easily :ref:`edit files in your local editor <edit_file>` on remote machines
* Automatic :opt:`re-use of existing connections <kitten-ssh.share_connections>` to avoid connection setup latency
<video controls width="640" height="360" poster="../_static/poster.png">
<source src="https://download.calibre-ebook.com/videos/kitty.mp4" type="video/mp4">
<source src="https://download.calibre-ebook.com/videos/kitty.webm" type="video/webm">
</video>
.. rst-class:: caption caption-text
Watch kitty in action!
Shell integration Shell integration
@@ -38,43 +30,268 @@ scrollback by pressing :sc:`scroll_to_previous_prompt` and clicking with the
mouse anywhere in the current command to move the cursor there. See mouse anywhere in the current command to move the cursor there. See
:doc:`shell-integration` for details. :doc:`shell-integration` for details.
Logos for windows
~~~~~~~~~~~~~~~~~~~~
kitty has the ability to display arbitrary ``PNG`` images at the corner
of any kitty window to serve as a *logo* for quick window identification.
Can be controlled via :opt:`window_logo_path` and also and changed via
the :ref:`at_set-window-logo` remote control command.
Useful to quickly identify special windows or just for some *bling*.
.. figure:: screenshots/window-logo.png
:alt: A screenshot of the kitty shell window showing the kitty logo
:align: center
:width: 100%
A screenshot of the kitty shell window showing the kitty logo
Visual keyboard based window select
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Select a kitty window visually using the keyboard, like the
:doc:`kittens/hints`, but for kitty windows. Can also be used from shell
scripts/third party integrations via the :ref:`at_select-window` remote control
command.
.. figure:: screenshots/select-window.png
:alt: A screenshot of the kitty select window function
:align: center
:width: 100%
Hints for selecting kitty windows visually
.. }}} .. }}}
Detailed list of changes Detailed list of changes
------------------------------------- -------------------------------------
0.26.2 [2022-09-05]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Allow creating :code:`overlay-main` windows, which are treated as the active window unlike normal overlays (:iss:`5392`)
- hints kitten: Allow using :doc:`launch` as the program to run, to open the result in a new kitty tab/window/etc. (:iss:`5462`)
- hyperlinked_grep kitten: Allow control over which parts of ``rg`` output are hyperlinked (:pull:`5428`)
- Fix regression in 0.26.0 that caused launching kitty without working STDIO handles to result in high CPU usage and prewarming failing (:iss:`5444`)
- :doc:`/launch`: Allow setting the margin and padding for newly created windows (:iss:`5463`)
- macOS: Fix regression in 0.26.0 that caused asking the user for a line of input such as for :ac:`set_tab_title` to not work (:iss:`5447`)
- hints kitten: hyperlink matching: Fix hints occasionally matching text on subsequent line as part of hyperlink (:pull:`5450`)
- Fix a regression in 0.26.0 that broke mapping of native keys whose key codes did not fit in 21 bits (:iss:`5452`)
- Wayland: Fix remembering window size not accurate when client side decorations are present
- Fix an issue where notification identifiers were not sanitized leading to
code execution if the user clicked on a notification popup from a malicious
source. Thanks to Carter Sande for discovering this vulnerability.
0.26.1 [2022-08-30]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ssh kitten: Fix executable permission missing from kitty bootstrap script (:iss:`5438`)
- Fix a regression in 0.26.0 that caused kitty to no longer set the ``LANG`` environment variable on macOS (:iss:`5439`)
- Allow specifying a title when using the :ac:`set_tab_title` action (:iss:`5441`)
0.26.0 [2022-08-29]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- A new option :opt:`remote_control_password` to use fine grained permissions for what can be remote controlled (:disc:`5320`)
- Reduce startup latency by ~30 milliseconds when running kittens via key bindings inside kitty (:iss:`5159`)
- A new option :opt:`modify_font` to adjust various font metrics like underlines, cell sizes etc. (:pull:`5265`)
- A new shortcut :sc:`show_kitty_doc` to display the kitty docs in a browser
- Graphics protocol: Only delete temp files if they have the string
:code:`tty-graphics-protocol` in their file paths. This prevents deletion of arbitrary files in :file:`/tmp`.
- Deprecate the ``adjust_baseline``, ``adjust_line_height`` and ``adjust_column_width`` options in favor of :opt:`modify_font`
- Wayland: Fix a regression in the previous release that caused mouse cursor
animation and keyboard repeat to stop working when switching seats (:iss:`5188`)
- Allow resizing windows created in session files (:pull:`5196`)
- Fix horizontal wheel events not being reported to client programs when they grab the mouse (:iss:`2819`)
- macOS: Remote control: Fix unable to launch a new OS window or background process when there is no OS window (:iss:`5210`)
- macOS: Fix unable to open new tab or new window when there is no OS window (:iss:`5276`)
- kitty @ set-colors: Fix changing inactive_tab_foreground not working (:iss:`5214`)
- macOS: Fix a regression that caused switching keyboard input using Eisu and
Kana keys not working (:iss:`5232`)
- Add a mappable action to toggle the mirrored setting for the tall and fat
layouts (:pull:`5344`)
- Add a mappable action to switch between predefined bias values for the tall and fat
layouts (:pull:`5352`)
- Wayland: Reduce flicker at startup by not using render frames immediately after a resize (:iss:`5235`)
- Linux: Update cursor position after all key presses not just pre-edit text
changes (:iss:`5241`)
- ssh kitten: Allow ssh kitten to work from inside tmux, provided the tmux
session inherits the correct KITTY env vars (:iss:`5227`)
- ssh kitten: A new option :code:`--symlink-strategy` to control how symlinks
are copied to the remote machine (:iss:`5249`)
- ssh kitten: Allow pressing :kbd:`Ctrl+C` to abort ssh before the connection is
completed (:iss:`5271`)
- Bash integration: Fix declare not creating global variables in .bashrc (:iss:`5254`)
- Bash integration: Fix the inherit_errexit option being set by shell integration (:iss:`5349`)
- :command:`kitty @ scroll-window` allow scrolling by fractions of a screen
(:iss:`5294`)
- remote files kitten: Fix working with files whose names have characters that
need to be quoted in shell scripts (:iss:`5313`)
- Expand ~ in paths configured in :opt:`editor` and :opt:`exe_search_path` (:disc:`5298`)
- Allow showing the working directory of the active window in tab titles
(:pull:`5314`)
- ssh kitten: Allow completion of ssh options between the destination and command (:iss:`5322`)
- macOS: Fix speaking selected text not working (:iss:`5357`)
- Allow ignoring failure to close windows/tabs via rc commands (:disc:`5406`)
- Fix hyperlinks not present when fetching text from the history buffer
(:iss:`5427`)
0.25.2 [2022-06-07]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- A new command :command:`edit-in-kitty` to :ref:`edit_file`
- Allow getting the last non-empty command output easily via an action or
remote control (:pull:`4973`)
- Fix a bug that caused :opt:`macos_colorspace` to always be ``default`` regardless of its actual value (:iss:`5129`)
- diff kitten: A new option :opt:`kitten-diff.ignore_name` to exclude files and directories from being scanned (:pull:`5171`)
- ssh kitten: Fix bash not being executed as a login shell since kitty 0.25.0 (:iss:`5130`)
- macOS: When pasting text and the clipboard has a filesystem path, paste the
full path instead of the text, which is sometimes just the file name (:pull:`5142`)
- macOS: Allow opening executables without a file extension with kitty as well
(:iss:`5160`)
- Themes kitten: Add a tab to show user defined custom color themes separately
(:pull:`5150`)
- Iosevka: Fix incorrect rendering when there is a combining char that does not
group with its neighbors (:iss:`5153`)
- Weston: Fix client side decorations flickering on slow computers during
window resize (:iss:`5162`)
- Remote control: Fix commands with large or asynchronous payloads like
:command:`kitty @ set-backround-image`, :command:`kitty @ set-window-logo`
and :command:`kitty @ select-window` not working correctly
when using a socket (:iss:`5165`)
- hints kitten: Fix surrounding quotes/brackets and embedded carriage returns
not being removed when using line number processing (:iss:`5170`)
0.25.1 [2022-05-26]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Shell integration: Add a command to :ref:`clone_shell`
- Remote control: Allow using :ref:`Boolean operators <search_syntax>` when constructing queries to match windows or tabs
- Sessions: Fix :code:`os_window_size` and :code:`os_window_class` not applying to the first OS Window (:iss:`4957`)
- Allow using the cwd of the oldest as well as the newest foreground process for :option:`launch --cwd` (:disc:`4869`)
- Bash integration: Fix the value of :opt:`shell_integration` not taking effect if the integration script is sourced in bashrc (:pull:`4964`)
- Fix a regression in the previous release that caused mouse move events to be incorrectly reported as drag events even when a button is not pressed (:iss:`4992`)
- remote file kitten: Integrate with the ssh kitten for improved performance
and robustness. Re-uses the control master connection of the ssh kitten to
avoid round-trip latency.
- Fix tab selection when closing a new tab not correct in some scenarios (:iss:`4987`)
- A new action :ac:`open_url` to open the specified URL (:pull:`5004`)
- A new option :opt:`select_by_word_characters_forward` that allows changing
which characters are considered part of a word to the right when double clicking to select
words (:pull:`5103`)
- macOS: Make the global menu shortcut to open kitty website configurable (:pull:`5004`)
- macOS: Add the :opt:`macos_colorspace` option to control what color space colors are rendered in (:iss:`4686`)
- Fix reloading of config not working when :file:`kitty.conf` does not exist when kitty is launched (:iss:`5071`)
- Fix deleting images by row not calculating image bounds correctly (:iss:`5081`)
- Increase the max number of combining chars per cell from two to three, without increasing memory usage.
- Linux: Load libfontconfig at runtime to allow the binaries to work for
running kittens on servers without FontConfig
- GNOME: Fix for high CPU usage caused by GNOME's text input subsystem going
into an infinite loop when IME cursor position is updated after a done event
(:iss:`5105`)
0.25.0 [2022-04-11]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- :doc:`kittens/ssh`: automatic shell integration when using SSH. Easily
clone local shell and editor configuration on remote machines, and automatic
re-use of existing connections to avoid connection setup latency.
- When pasting URLs at shell prompts automatically quote them. Also allow filtering pasted text and confirm pastes. See :opt:`paste_actions` for details. (:iss:`4873`)
- Change the default value of :opt:`confirm_os_window_close` to ask for confirmation when closing windows that are not sitting at shell prompts
- A new value :code:`last_reported` for :option:`launch --cwd` to use the current working directory last reported by the program running in the terminal
- macOS: When using Apple's less as the pager for viewing scrollback strip out OSC codes as it cant parse them (:iss:`4788`)
- diff kitten: Fix incorrect rendering in rare circumstances when scrolling after changing the context size (:iss:`4831`)
- icat kitten: Fix a regression that broke :option:`kitty +kitten icat --print-window-size` (:pull:`4818`)
- Wayland: Fix :opt:`hide_window_decorations` causing docked windows to be resized on blur (:iss:`4797`)
- Bash integration: Prevent shell integration code from running twice if user enables both automatic and manual integration
- Bash integration: Handle existing PROMPT_COMMAND ending with a literal newline
- Fix continued lines not having their continued status reset on line feed (:iss:`4837`)
- macOS: Allow the New kitty Tab/Window Here services to open multiple selected folders. (:pull:`4848`)
- Wayland: Fix a regression that broke IME when changing windows/tabs (:iss:`4853`)
- macOS: Fix Unicode paths not decoded correctly when dropping files (:pull:`4879`)
- Avoid flicker when starting kittens such as the hints kitten (:iss:`4674`)
- A new action :ac:`scroll_prompt_to_top` to move the current prompt to the top (:pull:`4891`)
- :ac:`select_tab`: Use stable numbers when selecting the tab (:iss:`4792`)
- Only check for updates in the official binary builds. Distro packages or source builds will no longer check for updates, regardless of the
value of :opt:`update_check_interval`.
- Fix :opt:`inactive_text_alpha` still being applied to the cursor hidden window after focus (:iss:`4928`)
- Fix resizing window that is extra tall/wide because of left-over cells not
working reliably (:iss:`4913`)
- A new action :ac:`close_other_tabs_in_os_window` to close other tabs in the active OS window (:pull:`4944`)
0.24.4 [2022-03-03]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Shell integration: Fix the default Bash :code:`$HISTFILE` changing to :file:`~/.sh_history` instead of :file:`~/.bash_history` (:iss:`4765`)
- Linux binaries: Fix binaries not working on systems with older Wayland client libraries (:iss:`4760`)
- Fix a regression in the previous release that broke kittens launched with :code:`STDIN` not connected to a terminal (:iss:`4763`)
- Wayland: Fix surface configure events not being acknowledged before commit
the resized buffer (:pull:`4768`)
0.24.3 [2022-02-28] 0.24.3 [2022-02-28]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -581,7 +798,7 @@ Detailed list of changes
0.21.2 [2021-06-28] 0.21.2 [2021-06-28]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- A new :opt:`adjust_baseline` option to adjust the vertical alignment of text - A new ``adjust_baseline`` option to adjust the vertical alignment of text
inside a line (:pull:`3734`) inside a line (:pull:`3734`)
- A new :opt:`url_excluded_characters` option to exclude additional characters - A new :opt:`url_excluded_characters` option to exclude additional characters
@@ -755,7 +972,7 @@ Detailed list of changes
scrollback buffer onto the screen. Useful, for example, to restore scrollback buffer onto the screen. Useful, for example, to restore
the screen after showing completions below the shell prompt. the screen after showing completions below the shell prompt.
- A new remote control command :ref:`at_env` to change the default - A new remote control command :ref:`at-env` to change the default
environment passed to newly created windows (:iss:`3529`) environment passed to newly created windows (:iss:`3529`)
- Linux: Fix binary kitty builds not able to load fonts in WOFF2 format - Linux: Fix binary kitty builds not able to load fonts in WOFF2 format
@@ -780,7 +997,7 @@ Detailed list of changes
kitty window title bar kitty window title bar
- Fix reading :option:`kitty --session` from ``STDIN`` not working when the - Fix reading :option:`kitty --session` from ``STDIN`` not working when the
:option:`kitty --detach` option is used (:iss:`3523`) :code:`kitty --detach` option is used (:iss:`3523`)
- Special case rendering of the few remaining Powerline box drawing chars - Special case rendering of the few remaining Powerline box drawing chars
(:iss:`3535`) (:iss:`3535`)
@@ -1923,7 +2140,7 @@ Detailed list of changes
- Allow the user to control the resize debounce time via - Allow the user to control the resize debounce time via
:opt:`resize_debounce_time`. :opt:`resize_debounce_time`.
- Remote control: Make the :ref:`at_set-font-size` command more capable. - Remote control: Make the :ref:`at-set-font-size` command more capable.
It can now increment font size and reset it. It also only acts on the It can now increment font size and reset it. It also only acts on the
active top-level window, by default (:iss:`1581`) active top-level window, by default (:iss:`1581`)
@@ -2232,7 +2449,7 @@ Detailed list of changes
- Fix a regression in 0.12.0 that broke dynamic resizing of layouts (:iss:`860`) - Fix a regression in 0.12.0 that broke dynamic resizing of layouts (:iss:`860`)
- Wayland: Allow using the :option:`kitty --class` command line flag - Wayland: Allow using the :code:`kitty --class` command line flag
to set the app id (:iss:`862`) to set the app id (:iss:`862`)
- Add completion of the kitty command for the fish shell (:pull:`829`) - Add completion of the kitty command for the fish shell (:pull:`829`)
@@ -2275,7 +2492,7 @@ Detailed list of changes
window is not the active window (:iss:`729`) window is not the active window (:iss:`729`)
- Remote control: Allow changing the current window layout with a new - Remote control: Allow changing the current window layout with a new
:ref:`at_goto-layout` command (:iss:`845`) :ref:`at-goto-layout` command (:iss:`845`)
- Remote control: Allow matching windows by the environment variables of their - Remote control: Allow matching windows by the environment variables of their
child process as well child process as well

View File

@@ -2,25 +2,25 @@ Saving and restoring colors
============================== ==============================
It is often useful for a full screen application with its own color themes to 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 set the default foreground, background, selection and cursor colors and the ANSI
ANSI color table. This allows for various performance optimizations when color table. This allows for various performance optimizations when drawing the
drawing the screen. The problem is that if the user previously used the escape screen. The problem is that if the user previously used the escape codes to
codes to change these colors herself, then running the full screen application change these colors themselves, then running the full screen application will
will lose her changes even after it exits. To avoid this, kitty introduces a lose those changes even after it exits. To avoid this, kitty introduces a new
new pair of *OSC* escape codes to push and pop the current color values from a pair of *OSC* escape codes to push and pop the current color values from a
stack:: stack::
<ESC>]30001<ESC>\ # push onto stack <ESC>]30001<ESC>\ # push onto stack
<ESC>]30101<ESC>\ # pop from stack <ESC>]30101<ESC>\ # pop from stack
These escape codes save/restore the colors, default These escape codes save/restore the colors, default background, default
background, default foreground, selection background, selection foreground and foreground, selection background, selection foreground and cursor color and the
cursor color and the 256 colors of the ANSI color table. 256 colors of the ANSI color table.
.. note:: In July 2020, after several years, XTerm copied this protocol .. note:: In July 2020, after several years, xterm copied this protocol
extension, without acknowledgement, and using incompatible escape codes extension, without acknowledgement, and using incompatible escape codes
(XTPUSHCOLORS, XTPOPCOLORS, XTREPORTCOLORS). And they decided to save not (XTPUSHCOLORS, XTPOPCOLORS, XTREPORTCOLORS). And they decided to save not
just the dynamic colors but the entire ANSI color table. In the interests of just the dynamic colors but the entire ANSI color table. In the interests of
promoting interoperability, kitty added support for XTerm's escape codes as promoting interoperability, kitty added support for xterm's escape codes as
well, and changed this extension to also save/restore the entire ANSI color well, and changed this extension to also save/restore the entire ANSI color
table. table.

View File

@@ -13,7 +13,7 @@ import subprocess
import sys import sys
import time import time
from functools import partial from functools import partial
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple from typing import Any, Callable, Dict, Iterable, List, Tuple
from docutils import nodes from docutils import nodes
from docutils.parsers.rst.roles import set_classes from docutils.parsers.rst.roles import set_classes
@@ -85,7 +85,7 @@ master_doc = 'index'
# #
# This is also used if you do content translation via gettext catalogs. # This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases. # Usually you set "language" from the command line for these cases.
language: Optional[str] = None language: str = 'en'
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # directories to ignore when looking for source files.
@@ -104,6 +104,10 @@ rst_prolog = '''
'''.replace('VERSION', str_version) '''.replace('VERSION', str_version)
smartquotes_action = 'qe' # educate quotes and ellipses but not dashes smartquotes_action = 'qe' # educate quotes and ellipses but not dashes
string_replacements = {
'_kitty_install_cmd': 'curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin',
}
# -- Options for HTML output ------------------------------------------------- # -- Options for HTML output -------------------------------------------------
@@ -116,10 +120,23 @@ html_title = 'kitty'
# Theme options are theme-specific and customize the look and feel of a theme # Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the # further. For a list of options available for each theme, see the
# documentation. # documentation.
# github_icon_path = 'M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z' # noqa
html_theme_options: Dict[str, Any] = { html_theme_options: Dict[str, Any] = {
'sidebar_hide_name': True, 'sidebar_hide_name': True,
'navigation_with_keys': True, 'navigation_with_keys': True,
'footer_icons': [
{
"name": "GitHub",
"url": "https://github.com/kovidgoyal/kitty",
"html": f"""
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="{github_icon_path}"></path>
</svg>
""",
"class": "",
},
],
} }
@@ -200,6 +217,13 @@ def commit_role(
def write_cli_docs(all_kitten_names: Iterable[str]) -> None: def write_cli_docs(all_kitten_names: Iterable[str]) -> None:
from kitty.cli import option_spec_as_rst from kitty.cli import option_spec_as_rst
from kitty.launch import options_spec as launch_options_spec from kitty.launch import options_spec as launch_options_spec
from kittens.ssh.copy import option_text
from kittens.ssh.options.definition import copy_message
with open('generated/ssh-copy.rst', 'w') as f:
f.write(option_spec_as_rst(
appname='copy', ospec=option_text, heading_char='^',
usage='file-or-dir-to-copy ...', message=copy_message
))
with open('generated/launch.rst', 'w') as f: with open('generated/launch.rst', 'w') as f:
f.write(option_spec_as_rst( f.write(option_spec_as_rst(
appname='launch', ospec=launch_options_spec, heading_char='_', appname='launch', ospec=launch_options_spec, heading_char='_',
@@ -225,7 +249,7 @@ if you specify a program-to-run you can use the special placeholder
from kitty.rc.base import cli_params_for from kitty.rc.base import cli_params_for
for cmd_name in sorted(all_command_names()): for cmd_name in sorted(all_command_names()):
func = command_for_name(cmd_name) func = command_for_name(cmd_name)
p(f'.. _at_{func.name}:\n') p(f'.. _at-{func.name}:\n')
p('kitty @', func.name) p('kitty @', func.name)
p('-' * 120) p('-' * 120)
p('.. program::', 'kitty @', func.name) p('.. program::', 'kitty @', func.name)
@@ -253,7 +277,7 @@ def write_remote_control_protocol_docs() -> None: # {{{
from kitty.rc.base import ( from kitty.rc.base import (
RemoteCommand, all_command_names, command_for_name RemoteCommand, all_command_names, command_for_name
) )
field_pat = re.compile(r'\s*([a-zA-Z0-9_+]+)\s*:\s*(.+)') field_pat = re.compile(r'\s*([a-zA-Z0-9_+/]+)\s*:\s*(.+)')
def format_cmd(p: Callable[..., None], name: str, cmd: RemoteCommand) -> None: def format_cmd(p: Callable[..., None], name: str, cmd: RemoteCommand) -> None:
p(name) p(name)
@@ -265,7 +289,7 @@ def write_remote_control_protocol_docs() -> None: # {{{
if m is None: if m is None:
p(line) p(line)
else: else:
fields.append((m.group(1), m.group(2))) fields.append((m.group(1).split('/')[0], m.group(2)))
if fields: if fields:
p('\nFields are:\n') p('\nFields are:\n')
for (name, desc) in fields: for (name, desc) in fields:
@@ -293,8 +317,16 @@ def write_remote_control_protocol_docs() -> None: # {{{
# }}} # }}}
def replace_string(app: Any, docname: str, source: List[str]) -> None: # {{{
src = source[0]
for k, v in app.config.string_replacements.items():
src = src.replace(k, v)
source[0] = src
# }}}
# config file docs {{{ # config file docs {{{
class ConfLexer(RegexLexer): # type: ignore class ConfLexer(RegexLexer): # type: ignore
name = 'Conf' name = 'Conf'
aliases = ['conf'] aliases = ['conf']
@@ -509,6 +541,8 @@ def setup(app: Any) -> None:
write_cli_docs(kn) write_cli_docs(kn)
write_remote_control_protocol_docs() write_remote_control_protocol_docs()
write_conf_docs(app, kn) write_conf_docs(app, kn)
app.add_config_value('string_replacements', {}, True)
app.connect('source-read', replace_string)
app.add_config_value('analytics_id', '', 'env') app.add_config_value('analytics_id', '', 'env')
app.connect('html-page-context', add_html_context) app.connect('html-page-context', add_html_context)
app.add_lexer('session', SessionLexer() if version_info[0] < 3 else SessionLexer) app.add_lexer('session', SessionLexer() if version_info[0] < 3 else SessionLexer)

View File

@@ -3,13 +3,14 @@ kitty.conf
.. highlight:: conf .. highlight:: conf
|kitty| is highly customizable, everything from keyboard shortcuts, to |kitty| is highly customizable, everything from keyboard shortcuts, to rendering
rendering frames-per-second. See below for an overview of all customization frames-per-second. See below for an overview of all customization possibilities.
possibilities.
You can open the config file within kitty by pressing :sc:`edit_config_file` (:kbd:`⌘+,` on macOS). You can open the config file within kitty by pressing :sc:`edit_config_file`
You can reload the config file within kitty by pressing (:kbd:`⌘+,` on macOS). A :file:`kitty.conf` with commented default
:sc:`reload_config_file` (:kbd:`⌃+⌘+,` on macOS) or sending kitty the ``SIGUSR1`` signal. configurations and descriptions will be created if the file does not exist.
You can reload the config file within kitty by pressing :sc:`reload_config_file`
(:kbd:`⌃+⌘+,` on macOS) or sending kitty the ``SIGUSR1`` signal.
You can also display the current configuration by pressing :sc:`debug_config` You can also display the current configuration by pressing :sc:`debug_config`
(:kbd:`⌥+⌘+,` on macOS). (:kbd:`⌥+⌘+,` on macOS).
@@ -17,26 +18,32 @@ You can also display the current configuration by pressing :sc:`debug_config`
|kitty| looks for a config file in the OS config directories (usually |kitty| looks for a config file in the OS config directories (usually
:file:`~/.config/kitty/kitty.conf`) but you can pass a specific path via the :file:`~/.config/kitty/kitty.conf`) but you can pass a specific path via the
:option:`kitty --config` option or use the ``KITTY_CONFIG_DIRECTORY`` :option:`kitty --config` option or use the :envvar:`KITTY_CONFIG_DIRECTORY`
environment variable. See the :option:`kitty --config` option for full details. environment variable. See :option:`kitty --config` for full details.
Comments can be added to the config file as lines starting with the ``#`` Comments can be added to the config file as lines starting with the ``#``
character. This works only if the ``#`` character is the first character character. This works only if the ``#`` character is the first character in the
in the line. line.
.. _include:
You can include secondary config files via the :code:`include` directive. If You can include secondary config files via the :code:`include` directive. If
you use a relative path for :code:`include`, it is resolved with respect to the you use a relative path for :code:`include`, it is resolved with respect to the
location of the current config file. Note that environment variables are location of the current config file. Note that environment variables are
expanded, so :code:`${USER}.conf` becomes :file:`name.conf` if expanded, so :code:`${USER}.conf` becomes :file:`name.conf` if
:code:`USER=name`. Also, you can use :code:`globinclude` to include files :code:`USER=name`. Also, you can use :code:`globinclude` to include files
matching a shell glob pattern. For example:: matching a shell glob pattern and :code:`envinclude` to include configuration
from environment variables. For example::
include other.conf include other.conf
# Include *.conf files from all subdirs of kitty.d inside the kitty config dir
globinclude kitty.d/**/*.conf globinclude kitty.d/**/*.conf
# Include the *contents* of all env vars starting with KITTY_CONF_
envinclude KITTY_CONF_*
.. note:: Syntax highlighting for :file:`kitty.conf` in vim is available via .. note:: Syntax highlighting for :file:`kitty.conf` in vim is available via
`vim-kitty <https://github.com/fladson/vim-kitty>`_. `vim-kitty <https://github.com/fladson/vim-kitty>`__.
.. include:: /generated/conf-kitty.rst .. include:: /generated/conf-kitty.rst
@@ -47,17 +54,17 @@ Sample kitty.conf
.. only:: html .. only:: html
You can download a sample :file:`kitty.conf` file with all default settings and You can download a sample :file:`kitty.conf` file with all default settings
comments describing each setting by clicking: :download:`sample kitty.conf and comments describing each setting by clicking: :download:`sample
</generated/conf/kitty.conf>`. kitty.conf </generated/conf/kitty.conf>`.
.. only:: man .. only:: man
You can edit a fully commented sample kitty.conf by pressing the You can edit a fully commented sample kitty.conf by pressing the
:sc:`edit_config_file` shortcut in kitty. This will generate a config :sc:`edit_config_file` shortcut in kitty. This will generate a config file
file with full documentation and all settings commented out. If you with full documentation and all settings commented out. If you have a
have a pre-existing kitty.conf, then that will be used instead, delete pre-existing :file:`kitty.conf`, then that will be used instead, delete it to
it to see the sample file. see the sample file.
All mappable actions All mappable actions

View File

@@ -3,19 +3,18 @@ Setting text styles/colors in arbitrary regions of the screen
There already exists an escape code to set *some* text attributes in arbitrary There already exists an escape code to set *some* text attributes in arbitrary
regions of the screen, `DECCARA regions of the screen, `DECCARA
<https://vt100.net/docs/vt510-rm/DECCARA.html>`_. However, it is limited to <https://vt100.net/docs/vt510-rm/DECCARA.html>`__. However, it is limited to
only a few attributes. |kitty| extends this to work with *all* SGR attributes. only a few attributes. |kitty| extends this to work with *all* SGR attributes.
So, for example, this can be used to set the background color in an arbitrary So, for example, this can be used to set the background color in an arbitrary
region of the screen. region of the screen.
The motivation for this extension is the various problems with the existing The motivation for this extension is the various problems with the existing
solution for erasing to background color, namely the *background color erase solution for erasing to background color, namely the *background color erase
(bce)* capability. See (bce)* capability. See :iss:`this discussion <160#issuecomment-346470545>`
:iss:`this discussion <160#issuecomment-346470545>` and `this FAQ <https://invisible-island.net/ncurses/ncurses.faq.html#bce_mismatches>`__
and `this FAQ <https://invisible-island.net/ncurses/ncurses.faq.html#bce_mismatches>`_
for a summary of problems with *bce*. for a summary of problems with *bce*.
For example, to set the background color to blue in a For example, to set the background color to blue in a rectangular region of the
rectangular region of the screen from (3, 4) to (10, 11), you use:: screen from (3, 4) to (10, 11), you use::
<ESC>[2*x<ESC>[4;3;11;10;44$r<ESC>[*x <ESC>[2*x<ESC>[4;3;11;10;44$r<ESC>[*x

View File

@@ -5,21 +5,20 @@ Desktop notifications
======================= =======================
|kitty| implements an extensible escape code (OSC 99) to show desktop |kitty| implements an extensible escape code (OSC 99) to show desktop
notifications. It is easy to use from shell scripts and fully extensible to notifications. It is easy to use from shell scripts and fully extensible to show
show title and body. Clicking on the notification can optionally focus the title and body. Clicking on the notification can optionally focus the window it
window it came from, and/or send an escape code back to the application running came from, and/or send an escape code back to the application running in that
in that window. window.
The design of the escape code is partially based on the discussion in The design of the escape code is partially based on the discussion in the
the defunct defunct `terminal-wg <https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/13>`__
`terminal-wg <https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/13>`_
The escape code has the form:: The escape code has the form::
<OSC> 99 ; metadata ; payload <terminator> <OSC> 99 ; metadata ; payload <terminator>
Here ``<OSC>`` is :code:`<ESC>]` and ``<terminator>`` is Here ``<OSC>`` is :code:`<ESC>]` and ``<terminator>`` is
:code:`<ESC><backslash>`. The metadata is a section of colon separated :code:`<ESC><backslash>`. The ``metadata`` is a section of colon separated
:code:`key=value` pairs. Every key must be a single character from the set :code:`key=value` pairs. Every key must be a single character from the set
:code:`a-zA-Z` and every value must be a word consisting of characters from :code:`a-zA-Z` and every value must be a word consisting of characters from
the set :code:`a-zA-Z0-9-_/\+.,(){}[]*&^%$#@!`~`. The payload must be the set :code:`a-zA-Z0-9-_/\+.,(){}[]*&^%$#@!`~`. The payload must be
@@ -45,28 +44,27 @@ The design of the escape code is fundamentally chunked, this is because
different terminal emulators have different limits on how large a single escape 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`` code can be. Chunking is accomplished by the ``i`` and ``d`` keys. The ``i``
key is the *notification id* which can be any string containing the characters key is the *notification id* which can be any string containing the characters
``[a-zA-Z0-9_-+.]``. The ``d`` key stands for *done* and ``[a-zA-Z0-9_-+.]``. The ``d`` key stands for *done* and can only take the
can only take the values ``0`` and ``1``. A value of ``0`` means the values ``0`` and ``1``. A value of ``0`` means the notification is not yet done
notification is not yet done and the terminal emulator should hold off and the terminal emulator should hold off displaying it. A value of ``1`` means
displaying it. A value of ``1`` means the notification is done, and should be the notification is done, and should be displayed. You can specify the title or
displayed. You can specify the title or body multiple times and the terminal body multiple times and the terminal emulator will concatenate them, thereby
emulator will concatenate them, thereby allowing arbitrarily long text allowing arbitrarily long text (terminal emulators are free to impose a sensible
(terminal emulators are free to impose a sensible limit to avoid limit to avoid Denial-of-Service attacks).
Denial-of-Service attacks).
Both the ``title`` and ``body`` payloads must be either UTF-8 encoded plain 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 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 which case there must be an ``e=1`` key in the metadata to indicate the payload
is base64 encoded. is Base64 encoded.
When the user clicks the notification, a couple of things can happen, the 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 terminal emulator can focus the window from which the notification came, and/or
it can send back an escape code to the application indicating the notification it can send back an escape code to the application indicating the notification
was activated. This is controlled by the ``a`` key which takes a comma was activated. This is controlled by the ``a`` key which takes a comma separated
separated set of values, ``report`` and ``focus``. The value ``focus`` means set of values, ``report`` and ``focus``. The value ``focus`` means focus the
focus the window from which the notification was issued and is the default. window from which the notification was issued and is the default. ``report``
``report`` means send an escape code back to the application. The format of the means send an escape code back to the application. The format of the returned
returned escape code is:: escape code is::
<OSC> 99 ; i=identifier ; <terminator> <OSC> 99 ; i=identifier ; <terminator>
@@ -87,9 +85,9 @@ to display it based on what it does understand.
.. note:: .. note::
It is possible to extend this escape code to allow specifying an icon for It is possible to extend this escape code to allow specifying an icon for
the notification, however, given that some platforms, such as macOS, dont the notification, however, given that some platforms, such as legacy versions
allow displaying custom icons on a notification, at all, it was decided to of macOS, don't allow displaying custom images on a notification, it was
leave it out of the spec for the time being. decided to leave it out of the spec for the time being.
Similarly, features such as scheduled notifications could be added in future Similarly, features such as scheduled notifications could be added in future
revisions. revisions.
@@ -107,7 +105,7 @@ Key Value Default Description
``d`` ``0`` or ``1`` ``1`` Indicates if the notification is ``d`` ``0`` or ``1`` ``1`` Indicates if the notification is
complete or not. complete or not.
``e`` ``0`` or ``1`` ``0`` If set to ``1`` means the payload is 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 otherwise it is plain UTF-8 text with no C0 control codes in it
``i`` ``[a-zA-Z0-9-_+.]`` ``0`` Identifier for the notification ``i`` ``[a-zA-Z0-9-_+.]`` ``0`` Identifier for the notification
@@ -118,5 +116,6 @@ Key Value Default Description
.. note:: .. note::
|kitty| also supports the legacy OSC 9 protocol developed by iTerm2 for |kitty| also supports the `legacy OSC 9 protocol developed by iTerm2
desktop notifications. <https://iterm2.com/documentation-escape-codes.html>`__ for desktop
notifications.

54
docs/extract-rst-targets.py Executable file
View File

@@ -0,0 +1,54 @@
#!/usr/bin/env python
# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>
import os
import re
from typing import Dict, Iterator
tgt_pat = re.compile(r'^.. _(\S+?):$', re.MULTILINE)
title_pat = re.compile('^(.+)\n[-=^#*]{5,}$', re.MULTILINE)
def find_explicit_targets(text: str) -> Iterator[str]:
for m in tgt_pat.finditer(text):
yield m.group(1)
def find_page_title(text: str) -> str:
for m in title_pat.finditer(text):
return m.group(1)
return ''
def main() -> Dict[str, Dict[str, str]]:
refs = {}
docs = {}
base = os.path.dirname(os.path.abspath(__file__))
for dirpath, dirnames, filenames in os.walk(base):
if 'generated' in dirnames:
dirnames.remove('generated')
for f in filenames:
if f.endswith('.rst'):
with open(os.path.join(dirpath, f)) as stream:
raw = stream.read()
href = os.path.relpath(stream.name, base).replace(os.sep, '/')
href = href.rpartition('.')[0] + '/'
docs[href.rstrip('/')] = find_page_title(raw)
first_line = raw.lstrip('\n').partition('\n')[0]
first_target_added = False
for explicit_target in find_explicit_targets(raw):
# Shorten the reference link to the top of the page.
# Note that anchor links should still be used in HTML docs
# to allow jumping within the same page.
if not first_target_added:
first_target_added = True
if first_line.startswith(f'.. _{explicit_target}:'):
refs[explicit_target] = href
continue
refs[explicit_target] = href + f'#{explicit_target.replace("_", "-")}'
return {'ref': refs, 'doc': docs}
if __name__ == '__main__':
import json
print(json.dumps(main(), indent=2))

View File

@@ -27,14 +27,15 @@ turned off for specific symbols using :opt:`narrow_symbols`.
Using a color theme with a background color does not work well in vim? Using a color theme with a background color does not work well in vim?
----------------------------------------------------------------------- -----------------------------------------------------------------------
First make sure you have not changed the TERM environment variable, it should First make sure you have not changed the :envvar:`TERM` environment variable, it
be ``xterm-kitty``. vim uses *background color erase* even if the terminfo file should be ``xterm-kitty``. vim uses *background color erase* even if the
does not contain the ``bce`` capability. This is a bug in vim. You can work around terminfo file does not contain the ``bce`` capability. This is a bug in vim. You
it by adding the following to your vimrc:: can work around it by adding the following to your vimrc::
let &t_ut='' let &t_ut=''
See :doc:`here <deccara>` for why |kitty| does not support background color erase. See :doc:`here <deccara>` for why |kitty| does not support background color
erase.
I get errors about the terminal being unknown or opening the terminal failing when SSHing into a different computer? I get errors about the terminal being unknown or opening the terminal failing when SSHing into a different computer?
@@ -46,42 +47,33 @@ terminfo files to the server::
kitty +kitten ssh myserver kitty +kitten ssh myserver
This ssh kitten takes all the same command line arguments This :doc:`ssh kitten <kittens/ssh>` takes all the same command line arguments
as ssh, you can alias it to ssh in your shell's rc files to avoid having to as :program:`ssh`, you can alias it to something small in your shell's rc files
type it each time:: to avoid having to type it each time::
alias ssh="kitty +kitten ssh" alias s="kitty +kitten ssh"
Remember to also setup :ref:`shell_integration` for completion and other If the ssh kitten fails, use the following one-liner instead (it is slower as it
niceties. needs to ssh into the server twice, but will work with most servers)::
If for some reason that does not work (typically because the server is using a
non POSIX compliant shell as ``/bin/sh``), you can try using it with ``python``
instead::
kitty +kitten ssh use-python myserver
If that also fails, perhaps because python is not installed on the remote
server, use the following one-liner instead (it
is slower as it needs to ssh into the server twice, but will work with most
servers)::
infocmp -a xterm-kitty | ssh myserver tic -x -o \~/.terminfo /dev/stdin infocmp -a xterm-kitty | ssh myserver tic -x -o \~/.terminfo /dev/stdin
If you are behind a proxy (like Balabit) that prevents this, you must redirect the If you are behind a proxy (like Balabit) that prevents this, or :program:`tic`
1st command to a file, copy that to the server and run ``tic`` manually. If you comes with macOS that does not support reading from STDIN, you must redirect the
connect to a server, embedded or Android system that doesn't have ``tic``, copy over first command to a file, copy that to the server and run :program:`tic`
your local file terminfo to the other system as :file:`~/.terminfo/x/xterm-kitty`. manually. If you connect to a server, embedded or Android system that doesn't
have :program:`tic`, copy over your local file terminfo to the other system as
:file:`~/.terminfo/x/xterm-kitty`.
Really, the correct solution for this is to convince the OpenSSH maintainers to Really, the correct solution for this is to convince the OpenSSH maintainers to
have ssh do this automatically, if possible, when connecting to a server, so that have :program:`ssh` do this automatically, if possible, when connecting to a
all terminals work transparently. server, so that all terminals work transparently.
If the server is running FreeBSD, or another system that relies on termcap If the server is running FreeBSD, or another system that relies on termcap
rather than terminfo, you will need to convert the terminfo file on your local rather than terminfo, you will need to convert the terminfo file on your local
machine by running (on local machine with |kitty|):: machine by running (on local machine with |kitty|)::
infocmp -C xterm-kitty infocmp -CrT0 xterm-kitty
The output of this command is the termcap description, which should be appended The output of this command is the termcap description, which should be appended
to :file:`/usr/share/misc/termcap` on the remote server. Then run the following to :file:`/usr/share/misc/termcap` on the remote server. Then run the following
@@ -93,22 +85,29 @@ command to apply your change (on the server)::
Keys such as arrow keys, backspace, delete, home/end, etc. do not work when using su or sudo? Keys such as arrow keys, backspace, delete, home/end, etc. do not work when using su or sudo?
------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------
Make sure the TERM environment variable, is ``xterm-kitty``. And either the Make sure the :envvar:`TERM` environment variable, is ``xterm-kitty``. And
TERMINFO environment variable points to a directory containing :file:`x/xterm-kitty` either the :envvar:`TERMINFO` environment variable points to a directory
or that file is under :file:`~/.terminfo/x/`. containing :file:`x/xterm-kitty` or that file is under :file:`~/.terminfo/x/`.
Note that ``sudo`` might remove TERMINFO. Then setting it at the shell prompt can For macOS, you may also need to put that file under :file:`~/.terminfo/78/`::
be too late, because command line editing may not be reinitialized. In that case
you can either ask ``sudo`` to set it or if that is not supported, insert an ``env`` mkdir -p ~/.terminfo/{78,x}
command before starting the shell, or, if not possible, after sudo start another ln -snf ../x/xterm-kitty ~/.terminfo/78/xterm-kitty
Shell providing the right terminfo path:: tic -x -o ~/.terminfo "$KITTY_INSTALLATION_DIR/terminfo/kitty.terminfo"
Note that :program:`sudo` might remove :envvar:`TERMINFO`. Then setting it at
the shell prompt can be too late, because command line editing may not be
reinitialized. In that case you can either ask :program:`sudo` to set it or if
that is not supported, insert an :program:`env` command before starting the
shell, or, if not possible, after sudo start another shell providing the right
terminfo path::
sudo … TERMINFO=$HOME/.terminfo bash -i sudo … TERMINFO=$HOME/.terminfo bash -i
sudo … env TERMINFO=$HOME/.terminfo bash -i sudo … env TERMINFO=$HOME/.terminfo bash -i
TERMINFO=/home/ORIGINALUSER/.terminfo exec bash -i TERMINFO=/home/ORIGINALUSER/.terminfo exec bash -i
You can configure sudo to preserve TERMINFO by running ``sudo You can configure :program:`sudo` to preserve :envvar:`TERMINFO` by running
visudo`` and adding the following line:: ``sudo visudo`` and adding the following line::
Defaults env_keep += "TERM TERMINFO" Defaults env_keep += "TERM TERMINFO"
@@ -132,12 +131,15 @@ You can also define keyboard shortcuts to set colors, for example::
map f1 set_colors --configured /path/to/some/config/file/colors.conf map f1 set_colors --configured /path/to/some/config/file/colors.conf
Or you can enable :doc:`remote control <remote-control>` for |kitty| and use :ref:`at_set-colors`. Or you can enable :doc:`remote control <remote-control>` for |kitty| and use
The shortcut mapping technique has the same syntax as the remote control :ref:`at-set-colors`. The shortcut mapping technique has the same syntax as the
command, for details, see :ref:`at_set-colors`. remote control command, for details, see :ref:`at-set-colors`.
To change colors when SSHing into a remote host, use the :opt:`color_scheme
<kitten-ssh.color_scheme>` setting for the :doc:`ssh kitten <kittens/ssh>`.
Additionally, You can use the Additionally, You can use the
`OSC terminal escape codes <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands>`_ `OSC terminal escape codes <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands>`__
to set colors. Examples of using OSC escape codes to set colors:: to set colors. Examples of using OSC escape codes to set colors::
Change the default foreground color: Change the default foreground color:
@@ -154,7 +156,7 @@ to set colors. Examples of using OSC escape codes to set colors::
printf '\x1b]4;n;green\x1b\\' printf '\x1b]4;n;green\x1b\\'
You can use various syntaxes/names for color specifications in the above You can use various syntaxes/names for color specifications in the above
examples. See `XParseColor <https://linux.die.net/man/3/xparsecolor>`_ examples. See `XParseColor <https://linux.die.net/man/3/xparsecolor>`__
for full details. for full details.
If a ``?`` is given rather than a color specification, kitty will respond If a ``?`` is given rather than a color specification, kitty will respond
@@ -167,14 +169,15 @@ How do I specify command line options for kitty on macOS?
Apple does not want you to use command line options with GUI applications. To Apple does not want you to use command line options with GUI applications. To
workaround that limitation, |kitty| will read command line options from the file workaround that limitation, |kitty| will read command line options from the file
:file:`<kitty config dir>/macos-launch-services-cmdline` when it is launched :file:`<kitty config dir>/macos-launch-services-cmdline` when it is launched
from the GUI, i.e. by clicking the |kitty| application icon or using ``open -a kitty``. from the GUI, i.e. by clicking the |kitty| application icon or using
Note that this file is *only read* when running via the GUI. ``open -a kitty``. Note that this file is *only read* when running via the GUI.
You can, of course, also run |kitty| from a terminal with command line options, using: You can, of course, also run |kitty| from a terminal with command line options,
:file:`/Applications/kitty.app/Contents/MacOS/kitty`. using: :file:`/Applications/kitty.app/Contents/MacOS/kitty`.
And within |kitty| itself, you can always run |kitty| using just ``kitty`` as it
cleverly adds itself to the :envvar:`PATH`.
And within |kitty| itself, you can always run |kitty| using just `kitty` as it
cleverly adds itself to the ``PATH``.
I catted a binary file and now kitty is hung? I catted a binary file and now kitty is hung?
----------------------------------------------- -----------------------------------------------
@@ -182,10 +185,10 @@ I catted a binary file and now kitty is hung?
**Never** output unknown binary data directly into a terminal. **Never** output unknown binary data directly into a terminal.
Terminals have a single channel for both data and control. Certain bytes Terminals have a single channel for both data and control. Certain bytes
are control codes. Some of these control codes are of arbitrary length, so are control codes. Some of these control codes are of arbitrary length, so if
if the binary data you output into the terminal happens to contain the starting the binary data you output into the terminal happens to contain the starting
sequence for one of these control codes, the terminal will hang waiting for sequence for one of these control codes, the terminal will hang waiting for the
the closing sequence. Press :kbd:`ctrl+shift+delete` to reset the terminal. closing sequence. Press :sc:`reset_terminal` to reset the terminal.
If you do want to cat unknown data, use ``cat -v``. If you do want to cat unknown data, use ``cat -v``.
@@ -193,29 +196,34 @@ If you do want to cat unknown data, use ``cat -v``.
kitty is not able to use my favorite font? kitty is not able to use my favorite font?
--------------------------------------------- ---------------------------------------------
|kitty| achieves its stellar performance by caching alpha masks of each |kitty| achieves its stellar performance by caching alpha masks of each rendered
rendered character on the GPU, and rendering them all in parallel. This means character on the GPU, and rendering them all in parallel. This means it is a
it is a strictly character cell based display. As such it can use only strictly character cell based display. As such it can use only monospace fonts,
monospace fonts, since every cell in the grid has to be the same size. since every cell in the grid has to be the same size. Furthermore, it needs
Furthermore, it needs fonts to be freely resizable, so it does not support fonts to be freely resizable, so it does not support bitmapped fonts.
bitmapped fonts.
.. note:: .. note::
If you are trying to use a font patched with NERD font symbols, dont do that If you are trying to use a font patched with `Nerd Fonts
as patching destroys fonts. There is no need, simply install the standalone <https://nerdfonts.com/>`__ symbols, don't do that as patching destroys
NERD font, kitty should pick up symbols from it automatically, and you can fonts. There is no need, simply install the standalone ``Symbols Nerd Font``
tell it to do so explicitly in case it doesnt with the :opt:`symbol_map` (the file :file:`NerdFontsSymbolsOnly.zip` from the `Nerd Fonts releases page
directive:: <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::
symbol_map U+23FB-U+23FE,U+2B58,U+E200-U+E2A9,U+E0A0-U+E0A3,U+E0B0-U+E0BF,U+E0C0-U+E0C8,U+E0CC-U+E0CF,U+E0D0-U+E0D2,U+E0D4,U+E700-U+E7C5,U+F000-U+F2E0,U+2665,U+26A1,U+F400-U+F4A8,U+F67C,U+E000-U+E00A,U+F300-U+F313,U+E5FA-U+E62B Symbols Nerd Font symbol_map U+23FB-U+23FE,U+2665,U+26A1,U+2B58,U+E000-U+E00A,U+E0A0-U+E0A3,U+E0B0-U+E0C8,U+E0CA,U+E0CC-U+E0D2,U+E0D4,U+E200-U+E2A9,U+E300-U+E3E3,U+E5FA-U+E62F,U+E700-U+E7C5,U+F000-U+F2E0,U+F300-U+F31C,U+F400-U+F4A9,U+F500-U+F8FF Symbols Nerd Font
Those Unicode symbols beyond the ``E000-F8FF`` Unicode private use area are
not included.
If your font is not listed in ``kitty +list-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:: 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 fc-list : family spacing outline scalable | grep -e spacing=100 -e spacing=90 | grep -e outline=True | grep -e scalable=True
Note that the spacing property is calculated by fontconfig based on actual Note that the spacing property is calculated by fontconfig based on actual glyph
glyph widths in the font. If for some reason fontconfig concludes your favorite 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 monospace font does not have ``spacing=100`` you can override it by using the
following :file:`~/.config/fontconfig/fonts.conf`:: following :file:`~/.config/fontconfig/fonts.conf`::
@@ -275,9 +283,13 @@ homepage:
:target: https://github.com/igrmk/whiskers :target: https://github.com/igrmk/whiskers
:width: 256 :width: 256
.. image:: https://github.com/samholmes/whiskers/raw/main/whiskers.png
:target: https://github.com/samholmes/whiskers
:width: 256
On macOS you can change the icon by following the steps: On macOS you can change the icon by following the steps:
#. Find :file:`kitty.app` in the Applications folder, select it and press :kbd:`⌘+i` #. Find :file:`kitty.app` in the Applications folder, select it and press :kbd:`⌘+I`
#. Drag :file:`kitty.icns` onto the application icon in the kitty info pane #. Drag :file:`kitty.icns` onto the application icon in the kitty info pane
#. Delete the icon cache and restart Dock:: #. Delete the icon cache and restart Dock::
@@ -297,7 +309,14 @@ the :sc:`send_text <send_text>` you can use the ``show_key`` kitten. Run::
kitty +kitten show_key kitty +kitten show_key
Then press the key you want to emulate. Then press the key you want to emulate. Note that this kitten will only show
keys that actually reach the terminal program, in particular, keys mapped to
actions in kitty will not be shown. To check those first map them to
:ac:`no_op`. You can also start a kitty instance without any shortcuts to
interfere::
kitty -o clear_all_shortcuts=yes kitty +kitten show_key
How do I open a new window or tab with the same working directory as the current window? How do I open a new window or tab with the same working directory as the current window?
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------
@@ -324,8 +343,8 @@ variables which kitty will now inherit.
You need to make sure that the environment variables you define in your shell's You need to make sure that the environment variables you define in your shell's
rc files are either also defined system wide or via the :opt:`env` directive in rc files are either also defined system wide or via the :opt:`env` directive in
:file:`kitty.conf`. Common environment variables that cause issues are those :file:`kitty.conf`. Common environment variables that cause issues are those
related to localization, such as ``LANG, LC_*`` and loading of configuration related to localization, such as :envvar:`LANG`, ``LC_*`` and loading of
files such as ``XDG_*, KITTY_CONFIG_DIRECTORY``. configuration files such as ``XDG_*``, :envvar:`KITTY_CONFIG_DIRECTORY`.
To see the environment variables that kitty sees, you can add the following To see the environment variables that kitty sees, you can add the following
mapping to :file:`kitty.conf`:: mapping to :file:`kitty.conf`::
@@ -342,40 +361,42 @@ sorts of places where they may or may not work.
I am using tmux and have a problem I am using tmux and have a problem
-------------------------------------- --------------------------------------
First, terminal multiplexers are :iss:`a bad idea <391#issuecomment-638320745>`, do First, terminal multiplexers are :iss:`a bad idea <391#issuecomment-638320745>`,
not use them, if at all possible. kitty contains features that do all of what do not use them, if at all possible. kitty contains features that do all of what
tmux does, but better, with the exception of remote persistence (:iss:`391`). tmux does, but better, with the exception of remote persistence (:iss:`391`).
If you still want to use tmux, read on. If you still want to use tmux, read on.
Image display will not work, see `tmux issue Image display will not work, see `tmux issue
<https://github.com/tmux/tmux/issues/1391>`_. <https://github.com/tmux/tmux/issues/1391>`__.
Using ancient versions of tmux such as 1.8 will Using ancient versions of tmux such as 1.8 will cause gibberish on screen when
cause gibberish on screen when pressing keys (:iss:`3541`). pressing keys (:iss:`3541`).
If you are using tmux with multiple terminals or you start it under one If you are using tmux with multiple terminals or you start it under one terminal
terminal and then switch to another and these terminals have different TERM and then switch to another and these terminals have different :envvar:`TERM`
variables, tmux will break. You will need to restart it as tmux does not variables, tmux will break. You will need to restart it as tmux does not support
support multiple terminfo definitions. multiple terminfo definitions.
If you use any of the advanced features that kitty has innovated, such as If you use any of the advanced features that kitty has innovated, such as
styled underlines, desktop notifications, extended keyboard support, etc. :doc:`styled underlines </underlines>`, :doc:`desktop notifications
they may or may not work, depending on the whims of tmux's maintainer, your </desktop-notifications>`, :doc:`extended keyboard support
version of tmux, etc. </keyboard-protocol>`, etc. they may or may not work, depending on the whims of
tmux's maintainer, your version of tmux, etc.
I opened and closed a lot of windows/tabs and top shows kitty's memory usage is very high? I opened and closed a lot of windows/tabs and top shows kitty's memory usage is very high?
------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------
``top`` is not a good way to measure process memory usage. That is because on :program:`top` is not a good way to measure process memory usage. That is
modern systems, when allocating memory to a process, the C library functions because on modern systems, when allocating memory to a process, the C library
will typically allocate memory in large blocks, and give the process chunks of functions will typically allocate memory in large blocks, and give the process
these blocks. When the process frees a chunk, the C library will not chunks of these blocks. When the process frees a chunk, the C library will not
necessarily release the underlying block back to the OS. So even though the necessarily release the underlying block back to the OS. So even though the
application has released the memory, ``top`` will still claim the process is application has released the memory, :program:`top` will still claim the process
using it. is using it.
To check for memory leaks, instead use a tool like ``valgrind``. Run:: To check for memory leaks, instead use a tool like `Valgrind
<https://valgrind.org/>`__. Run::
PYTHONMALLOC=malloc valgrind --tool=massif kitty PYTHONMALLOC=malloc valgrind --tool=massif kitty
@@ -388,18 +409,19 @@ that window, maybe run yes or find again. Then quit kitty and run::
You will see the allocations graph goes up when you opened the windows, then You will see the allocations graph goes up when you opened the windows, then
goes back down when you closed them, indicating there were no memory leaks. goes back down when you closed them, indicating there were no memory leaks.
For those interested, you can get a similar profile out of ``valgrind`` as you get For those interested, you can get a similar profile out of :program:`valgrind`
with ``top`` by adding ``--pages-as-heap=yes`` then you will see that memory as you get with :program:`top` by adding ``--pages-as-heap=yes`` then you will
allocated in malloc is not freed in free. This can be further refined if you see that memory allocated in malloc is not freed in free. This can be further
use `glibc`` as your C library by setting the environment variable refined if you use ``glibc`` as your C library by setting the environment
``MALLOC_MMAP_THRESHOLD_=64``. This will cause free to actually free memory variable ``MALLOC_MMAP_THRESHOLD_=64``. This will cause free to actually free
allocated in sizes of more than 64 bytes. With this set, memory usage will memory allocated in sizes of more than 64 bytes. With this set, memory usage
climb high, then fall when closing windows, but not fall all the way back. The will climb high, then fall when closing windows, but not fall all the way back.
remaining used memory can be investigated using valgrind again, and it will The remaining used memory can be investigated using valgrind again, and it will
come from arenas in the GPU drivers and the per thread arenas glibc's malloc come from arenas in the GPU drivers and the per thread arenas glibc's malloc
maintains. These too allocate memory in large blocks and dont release it back maintains. These too allocate memory in large blocks and don't release it back
to the OS immediately. to the OS immediately.
Why does kitty sometimes start slowly on my Linux system? Why does kitty sometimes start slowly on my Linux system?
------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------
@@ -423,4 +445,4 @@ The correct command will depend on your situation and hardware.
:file:`libEGL_mesa.so` and ignore :file:`libEGL_nvidia.so` also available on the :file:`libEGL_mesa.so` and ignore :file:`libEGL_nvidia.so` also available on the
system, which will wake the NVIDIA card during device enumeration. system, which will wake the NVIDIA card during device enumeration.
``MESA_LOADER_DRIVER_OVERRIDE`` also assures that Mesa won't offer any NVIDIA ``MESA_LOADER_DRIVER_OVERRIDE`` also assures that Mesa won't offer any NVIDIA
card during enumeration, and will instead just use `/lib/dri/radeonsi_dri.so`. card during enumeration, and will instead just use :file:`radeonsi_dri.so`.

View File

@@ -30,14 +30,20 @@ Glossary
overlay overlay
An *overlay window* is a :term:`kitty window <window>` that is placed on An *overlay window* is a :term:`kitty window <window>` that is placed on
top of an existing kitty window, entirely covering it. Overlays are used top of an existing kitty window, entirely covering it. Overlays are used
throught kitty, for example, to display the :ref:`the scrollback buffer <scrollback>`, throughout kitty, for example, to display the :ref:`the scrollback buffer <scrollback>`,
to display :doc:`hints </kittens/hints>`, for :doc:`unicode input to display :doc:`hints </kittens/hints>`, for :doc:`unicode input
</kittens/unicode_input>` etc. </kittens/unicode_input>` etc. Normal overlays are meant for short
duration popups and so are not considered the :italic:`active window`
when determining the current working directory or getting input text for
kittens, launch commands, etc. To create an overlay considered as a
:italic:`main window` use the :code:`overlay-main` argument to
:doc:`launch`.
hyperlinks hyperlinks
Terminals can have hyperlinks, just like the internet. In kitty you can Terminals can have hyperlinks, just like the internet. In kitty you can
:doc:`control exactly what happens <open_actions>` when clicking on a :doc:`control exactly what happens <open_actions>` when clicking on a
hyperlink, based on the type of link and its URL. hyperlink, based on the type of link and its URL. See also `Hyperlinks in terminal
emulators <https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>`__.
.. _env_vars: .. _env_vars:
@@ -45,6 +51,7 @@ Environment variables
------------------------ ------------------------
Variables that influence kitty behavior Variables that influence kitty behavior
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. envvar:: KITTY_CONFIG_DIRECTORY .. envvar:: KITTY_CONFIG_DIRECTORY
@@ -57,11 +64,17 @@ Variables that influence kitty behavior
Controls where kitty stores cache files. Defaults to :file:`~/.cache/kitty` Controls where kitty stores cache files. Defaults to :file:`~/.cache/kitty`
or :file:`~/Library/Caches/kitty` on macOS. or :file:`~/Library/Caches/kitty` on macOS.
.. envvar:: KITTY_RUNTIME_DIRECTORY
Controls where kitty stores runtime files like sockets. Defaults to
the :code:`XDG_RUNTIME_DIR` environment variable if that is defined
otherwise the run directory inside the kitty cache directory is used.
.. envvar:: VISUAL .. envvar:: VISUAL
The terminal editor (such as ``vi`` or ``nano``) kitty uses, when, for The terminal based text editor (such as :program:`vi` or :program:`nano`)
instance, opening :file:`kitty.conf` in response to :sc:`edit_config_file`. kitty uses, when, for instance, opening :file:`kitty.conf` in response to
:sc:`edit_config_file`.
.. envvar:: EDITOR .. envvar:: EDITOR
@@ -78,84 +91,124 @@ Variables that influence kitty behavior
is possible for the autodiscovery to fail; the default Wayland XKB mappings is possible for the autodiscovery to fail; the default Wayland XKB mappings
are used in this case. See :pull:`3943` for details. are used in this case. See :pull:`3943` for details.
.. envvar:: SSH_ASKPASS
Specify the program for SSH to ask for passwords. When this is set, :doc:`ssh
kitten </kittens/ssh>` will use this environment variable by default. See
:opt:`askpass <kitten-ssh.askpass>` for details.
.. envvar:: KITTY_CLONE_SOURCE_CODE
Set this to some shell code that will be executed in the cloned window with
:code:`eval` when :ref:`clone-in-kitty <clone_shell>` is used.
.. envvar:: KITTY_CLONE_SOURCE_PATH
Set this to the path of a file that will be sourced in the cloned window when
:ref:`clone-in-kitty <clone_shell>` is used.
.. envvar:: KITTY_DEVELOP_FROM
Set this to the directory path of the kitty source code and its Python code
will be loaded from there. Only works with official binary builds.
.. envvar:: KITTY_RC_PASSWORD
Set this to a pass phrase to use the ``kitty @`` remote control command with
:opt:`remote_control_password`.
Variables that kitty sets when running child programs Variables that kitty sets when running child programs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. envvar:: LANG .. envvar:: LANG
This is set only on macOS, and only if the country and language from the This is only set on macOS. If the country and language from the macOS user
macOS user settings form a valid locale. settings form an invalid locale, it will be set to :code:`en_US.UTF-8`.
.. envvar:: PATH
kitty prepends itself to the PATH of its own environment to ensure the
functions calling :program:`kitty` will work properly.
.. envvar:: KITTY_WINDOW_ID .. envvar:: KITTY_WINDOW_ID
An integer that is the id for the kitty :term:`window` the program is running in. An integer that is the id for the kitty :term:`window` the program is running in.
Can be used with the :doc:`kitty remote control facility <remote-control>`. Can be used with the :doc:`kitty remote control facility <remote-control>`.
.. envvar:: KITTY_PID .. envvar:: KITTY_PID
An integer that is the process id for the kitty process in which the program An integer that is the process id for the kitty process in which the program
is running. Allows programs to tell kitty to reload its config by sending it is running. Allows programs to tell kitty to reload its config by sending it
the SIGUSR1 signal. the SIGUSR1 signal.
.. envvar:: KITTY_PUBLIC_KEY
A public key that programs can use to communicate securely with kitty using
the remote control protocol. The format is: :code:`protocol:key data`.
.. envvar:: WINDOWID .. envvar:: WINDOWID
The id for the :term:`OS Window <os_window>` the program is running in. Only available The id for the :term:`OS Window <os_window>` the program is running in. Only available
on platforms that have ids for their windows, such as X11 and macOS. on platforms that have ids for their windows, such as X11 and macOS.
.. envvar:: TERM .. envvar:: TERM
The name of the terminal, defaults to ``xterm-kitty``. See :opt:`term`. The name of the terminal, defaults to ``xterm-kitty``. See :opt:`term`.
.. envvar:: TERMINFO .. envvar:: TERMINFO
Path to a directory containing the kitty terminfo database. Path to a directory containing the kitty terminfo database.
.. envvar:: KITTY_INSTALLATION_DIR .. envvar:: KITTY_INSTALLATION_DIR
Path to the kitty installation directory. Path to the kitty installation directory.
.. envvar:: COLORTERM .. envvar:: COLORTERM
Set to the value ``truecolor`` to indicate that kitty supports 16 million Set to the value ``truecolor`` to indicate that kitty supports 16 million
colors. colors.
.. envvar:: KITTY_LISTEN_ON .. envvar:: KITTY_LISTEN_ON
Set when the :doc:`remote control <remote-control>` facility is enabled and Set when the :doc:`remote control <remote-control>` facility is enabled and
the a socket is used for control via :option:`kitty --listen-on` or :opt:`listen_on`. the a socket is used for control via :option:`kitty --listen-on` or :opt:`listen_on`.
Contains the path to the socket. Avoids needs to use :option:`kitty @ --to` when Contains the path to the socket. Avoid the need to use :option:`kitty @ --to` when
issuing remote control commands. issuing remote control commands.
.. envvar:: KITTY_PIPE_DATA .. envvar:: KITTY_PIPE_DATA
Set to data describing the layout of the screen when running child Set to data describing the layout of the screen when running child
programs using :option:`launch --stdin-source` with the contents of the programs using :option:`launch --stdin-source` with the contents of the
screen/scrollback piped to them. screen/scrollback piped to them.
.. envvar:: KITTY_CHILD_CMDLINE .. envvar:: KITTY_CHILD_CMDLINE
Set to the command line of the child process running in the kitty Set to the command line of the child process running in the kitty
window when calling the notification callback program on terminal bell, see window when calling the notification callback program on terminal bell, see
:opt:`command_on_bell`. :opt:`command_on_bell`.
.. envvar:: KITTY_COMMON_OPTS .. envvar:: KITTY_COMMON_OPTS
Set with the values of some common kitty options when running Set with the values of some common kitty options when running
kittens, so kittens can use them without needing to load kitty.conf. kittens, so kittens can use them without needing to load :file:`kitty.conf`.
.. envvar:: KITTY_SHELL_INTEGRATION .. envvar:: KITTY_SHELL_INTEGRATION
Set when enabling :ref:`shell_integration`. It is automatically removed by Set when enabling :ref:`shell_integration`. It is automatically removed by
the shell integration scripts. the shell integration scripts.
.. envvar:: ZDOTDIR
Set when enabling :ref:`shell_integration` with :program:`zsh`, allowing
:program:`zsh` to automatically load the integration script.
.. envvar:: XDG_DATA_DIRS
Set when enabling :ref:`shell_integration` with :program:`fish`, allowing
:program:`fish` to automatically load the integration script.
.. envvar:: ENV
Set when enabling :ref:`shell_integration` with :program:`bash`, allowing
:program:`bash` to automatically load the integration script.

View File

@@ -31,6 +31,7 @@ Some programs and libraries that use the kitty graphics protocol:
* `ranger <https://github.com/ranger/ranger>`_ - a terminal file manager, with * `ranger <https://github.com/ranger/ranger>`_ - a terminal file manager, with
image previews, see this `PR <https://github.com/ranger/ranger/pull/1077>`_ image previews, see this `PR <https://github.com/ranger/ranger/pull/1077>`_
* :doc:`kitty-diff <kittens/diff>` - a side-by-side terminal diff program with support for images * :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
* `pixcat <https://github.com/mirukana/pixcat>`_ - a third party CLI and python library that wraps the graphics protocol * `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 * `neofetch <https://github.com/dylanaraps/neofetch>`_ - A command line system
information tool information tool
@@ -42,11 +43,13 @@ Some programs and libraries that use the kitty graphics protocol:
* `rasterm <https://github.com/BourgeoisBear/rasterm>`_ - Go library to display images in the terminal * `rasterm <https://github.com/BourgeoisBear/rasterm>`_ - Go library to display images in the terminal
* `chafa <https://github.com/hpjansson/chafa>`_ - a terminal image viewer * `chafa <https://github.com/hpjansson/chafa>`_ - a terminal image viewer
* `hologram.nvim <https://github.com/edluffy/hologram.nvim>`_ - view images inside nvim * `hologram.nvim <https://github.com/edluffy/hologram.nvim>`_ - view images inside nvim
* `term-image <https://github.com/AnonymouX47/term-image>`_ - A Python library, CLI and TUI to display and browse images in the terminal
Other terminals that have implemented the graphics protocol: Other terminals that have implemented the graphics protocol:
* `WezTerm <https://github.com/wez/wezterm/issues/986>`_ * `WezTerm <https://github.com/wez/wezterm/issues/986>`_
* `Konsole <https://invent.kde.org/utilities/konsole/-/merge_requests/594>`_ * `Konsole <https://invent.kde.org/utilities/konsole/-/merge_requests/594>`_
* `wayst <https://github.com/91861/wayst>`_
Getting the window size Getting the window size
@@ -234,7 +237,8 @@ Value of `t` Meaning
the terminal emulator should only delete the file if it the terminal emulator should only delete the file if it
is in a known temporary directory, such as :file:`/tmp`, is in a known temporary directory, such as :file:`/tmp`,
:file:`/dev/shm`, :file:`TMPDIR env var if present` and any platform :file:`/dev/shm`, :file:`TMPDIR env var if present` and any platform
specific temporary directories. specific temporary directories and the file has the
string :code:`tty-graphics-protocol` in its full file path.
``s`` A *shared memory object*, which on POSIX systems is a ``s`` A *shared memory object*, which on POSIX systems is a
`POSIX shared memory object <https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html>`_ `POSIX shared memory object <https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html>`_
and on Windows is a and on Windows is a
@@ -326,14 +330,14 @@ use the *query action*, set ``a=q``. Then the terminal emulator will try to load
the image and respond with either OK or an error, as above, but it will not the image and respond with either OK or an error, as above, but it will not
replace an existing image with the same id, nor will it store the image. replace an existing image with the same id, nor will it store the image.
As of September 2021, kitty and WezTerm are the only terminal emulators to support this As of April 2022, kitty and WezTerm are the only terminal emulators to
graphics protocol. We intend that any terminal emulator that wishes to support support this graphics protocol completely, with Konsole and wayst having partial support.
it can do so. To check if a terminal emulator supports the graphics protocol the best way We intend that any terminal emulator that wishes to support it can do so. To
is to send the above *query action* followed by a request for the check if a terminal emulator supports the graphics protocol the best way is to
`primary device attributes <https://vt100.net/docs/vt510-rm/DA1.html>`_. If you send the above *query action* followed by a request for the `primary device
get back an answer for the device attributes without getting back an answer for attributes <https://vt100.net/docs/vt510-rm/DA1.html>`_. If you get back an
the *query action* the terminal emulator does not support the graphics answer for the device attributes without getting back an answer for the *query
protocol. action* the terminal emulator does not support the graphics protocol.
This means that terminal emulators that support the graphics protocol, **must** This means that terminal emulators that support the graphics protocol, **must**
reply to *query actions* immediately without processing other input. Most reply to *query actions* immediately without processing other input. Most
@@ -376,7 +380,9 @@ id. To do so add the ``p`` key with a number between ``1`` and ``4294967295``.
When you specify a placement id, it will be added to the acknowledgement code When you specify a placement id, it will be added to the acknowledgement code
above. Every placement is uniquely identified by the pair of the ``image id`` above. Every placement is uniquely identified by the pair of the ``image id``
and the ``placement id``. If you specify a placement id for an image that does and the ``placement id``. If you specify a placement id for an image that does
not have an id, it will be ignored. An example response:: not have an id (i.e. has id=0), it will be ignored. In particular this means
there can exist multiple images with ``image id=0, placement id=0``.
An example response::
<ESC>_Gi=<image id>,p=<placement id>;OK<ESC>\ <ESC>_Gi=<image id>,p=<placement id>;OK<ESC>\
@@ -413,7 +419,9 @@ they are semi-transparent. You can specify z-index values using the ``z`` key.
Negative z-index values mean that the images will be drawn under the text. This Negative z-index values mean that the images will be drawn under the text. This
allows rendering of text on top of images. Negative z-index values below allows rendering of text on top of images. Negative z-index values below
INT32_MIN/2 (-1,073,741,824) will be drawn under cells with non-default background INT32_MIN/2 (-1,073,741,824) will be drawn under cells with non-default background
colors. colors. If two images with the same z-index overlap then the image with the
lower id is considered to have the lower z-index. If the images have the same
z-index and the same id, then the behavior is undefined.
.. note:: After placing an image on the screen the cursor must be moved to the .. note:: After placing an image on the screen the cursor must be moved to the
right by the number of cols in the image placement rectangle and down by the right by the number of cols in the image placement rectangle and down by the

View File

@@ -1,261 +0,0 @@
#!/usr/bin/env python3
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
from __future__ import (
absolute_import, division, print_function, unicode_literals
)
import atexit
import json
import os
import platform
import re
import shlex
import shutil
import subprocess
import sys
import tempfile
py3 = sys.version_info[0] > 2
is64bit = platform.architecture()[0] == '64bit'
is_macos = 'darwin' in sys.platform.lower()
is_linux_arm = is_linux_arm64 = False
if is_macos:
mac_ver = tuple(map(int, platform.mac_ver()[0].split('.')))
if mac_ver[:2] < (10, 12):
raise SystemExit('Your version of macOS is too old, at least 10.12 is required')
else:
machine = (os.uname()[4] or '').lower()
if machine.startswith('arm') or machine.startswith('aarch64'):
is_linux_arm = True
is_linux_arm64 = machine.startswith('arm64') or machine.startswith('aarch64')
try:
__file__
from_file = True
except NameError:
from_file = False
if py3:
unicode = str
raw_input = input
import urllib.request as urllib
def encode_for_subprocess(x):
return x
else:
from future_builtins import map
import urllib2 as urllib
def encode_for_subprocess(x):
if isinstance(x, unicode):
x = x.encode('utf-8')
return x
def run(*args):
if len(args) == 1:
args = shlex.split(args[0])
args = list(map(encode_for_subprocess, args))
ret = subprocess.Popen(args).wait()
if ret != 0:
raise SystemExit(ret)
class Reporter: # {{{
def __init__(self, fname):
self.fname = fname
self.last_percent = 0
def __call__(self, blocks, block_size, total_size):
percent = (blocks*block_size)/float(total_size)
report = '\rDownloaded {:.1%} '.format(percent)
if percent - self.last_percent > 0.05:
self.last_percent = percent
print(report, end='')
sys.stdout.flush()
# }}}
def get_release_data(relname='latest'):
print('Checking for latest release on GitHub...')
req = urllib.Request(
'https://api.github.com/repos/kovidgoyal/kitty/releases/' + relname,
headers={'Accept': 'application/vnd.github.v3+json'})
try:
res = urllib.urlopen(req).read().decode('utf-8')
except Exception as err:
raise SystemExit('Failed to contact {} with error: {}'.format(req.get_full_url(), err))
data = json.loads(res)
html_url = data['html_url'].replace('/tag/', '/download/').rstrip('/')
for asset in data.get('assets', ()):
name = asset['name']
if is_macos:
if name.endswith('.dmg'):
return html_url + '/' + name, asset['size']
else:
if name.endswith('.txz'):
if is64bit:
q = '-arm64.txz' if is_linux_arm64 else '-x86_64.txz'
if name.endswith(q):
return html_url + '/' + name, asset['size']
else:
if name.endswith('-i686.txz'):
return html_url + '/' + name, asset['size']
raise SystemExit('Failed to find the installer package on github')
def do_download(url, size, dest):
print('Will download and install', os.path.basename(dest))
reporter = Reporter(os.path.basename(dest))
# Get content length and check if range is supported
rq = urllib.urlopen(url)
headers = rq.info()
sent_size = int(headers['content-length'])
if sent_size != size:
raise SystemExit('Failed to download from {} Content-Length ({}) != {}'.format(url, sent_size, size))
with open(dest, 'wb') as f:
while f.tell() < size:
raw = rq.read(8192)
if not raw:
break
f.write(raw)
reporter(f.tell(), 1, size)
rq.close()
if os.path.getsize(dest) < size:
raise SystemExit('Download failed, try again later')
print('\rDownloaded {} bytes'.format(os.path.getsize(dest)))
def clean_cache(cache, fname):
for x in os.listdir(cache):
if fname not in x:
os.remove(os.path.join(cache, x))
def download_installer(url, size):
fname = url.rpartition('/')[-1]
tdir = tempfile.gettempdir()
cache = os.path.join(tdir, 'kitty-installer-cache')
if not os.path.exists(cache):
os.makedirs(cache)
clean_cache(cache, fname)
dest = os.path.join(cache, fname)
if os.path.exists(dest) and os.path.getsize(dest) == size:
print('Using previously downloaded', fname)
return dest
if os.path.exists(dest):
os.remove(dest)
do_download(url, size, dest)
return dest
def macos_install(dmg, dest='/Applications', launch=True):
mp = tempfile.mkdtemp()
atexit.register(shutil.rmtree, mp)
run('hdiutil', 'attach', dmg, '-mountpoint', mp)
try:
os.chdir(mp)
app = 'kitty.app'
d = os.path.join(dest, app)
if os.path.exists(d):
shutil.rmtree(d)
dest = os.path.join(dest, app)
run('ditto', '-v', app, dest)
print('Successfully installed kitty into', dest)
if launch:
run('open', dest)
finally:
os.chdir('/')
run('hdiutil', 'detach', mp)
def linux_install(installer, dest=os.path.expanduser('~/.local'), launch=True):
dest = os.path.join(dest, 'kitty.app')
if os.path.exists(dest):
shutil.rmtree(dest)
os.makedirs(dest)
print('Extracting tarball...')
run('tar', '-C', dest, '-xJof', installer)
print('kitty successfully installed to', dest)
kitty = os.path.join(dest, 'bin', 'kitty')
print('Use', kitty, 'to run kitty')
if launch:
run(kitty, '--detach')
def main(dest=None, launch=True, installer=None):
if not dest:
if is_macos:
dest = '/Applications'
else:
dest = os.path.expanduser('~/.local')
if is_linux_arm and not is_linux_arm64:
raise SystemExit(
'You are running on a 32-bit ARM system. The kitty binaries are only'
' available for 64 bit ARM systems. You will have to build from'
' source.')
if not installer:
url, size = get_release_data()
installer = download_installer(url, size)
else:
if installer == 'nightly':
url, size = get_release_data('tags/nightly')
installer = download_installer(url, size)
else:
installer = os.path.abspath(installer)
if not os.access(installer, os.R_OK):
raise SystemExit('Could not read from: {}'.format(installer))
if is_macos:
macos_install(installer, dest=dest, launch=launch)
else:
linux_install(installer, dest=dest, launch=launch)
def script_launch():
# To test: python3 -c "import runpy; runpy.run_path('installer.py', run_name='script_launch')"
def path(x):
return os.path.expandvars(os.path.expanduser(x))
def to_bool(x):
return x.lower() in {'y', 'yes', '1', 'true'}
type_map = {x: path for x in 'dest installer'.split()}
type_map['launch'] = to_bool
kwargs = {}
for arg in sys.argv[1:]:
if arg:
m = re.match('([a-z_]+)=(.+)', arg)
if m is None:
raise SystemExit('Unrecognized command line argument: ' + arg)
k = m.group(1)
if k not in type_map:
raise SystemExit('Unrecognized command line argument: ' + arg)
kwargs[k] = type_map[k](m.group(2))
main(**kwargs)
def update_installer_wrapper():
# To run: python3 -c "import runpy; runpy.run_path('installer.py', run_name='update_wrapper')" installer.sh
with open(__file__, 'rb') as f:
src = f.read().decode('utf-8')
wrapper = sys.argv[-1]
with open(wrapper, 'r+b') as f:
raw = f.read().decode('utf-8')
nraw = re.sub(r'^# HEREDOC_START.+^# HEREDOC_END', lambda m: '# HEREDOC_START\n{}\n# HEREDOC_END'.format(src), raw, flags=re.MULTILINE | re.DOTALL)
if 'update_intaller_wrapper()' not in nraw:
raise SystemExit('regex substitute of HEREDOC failed')
f.seek(0), f.truncate()
f.write(nraw.encode('utf-8'))
if __name__ == '__main__' and from_file:
main()
elif __name__ == 'update_wrapper':
update_installer_wrapper()
elif __name__ == 'script_launch':
script_launch()

View File

@@ -1,292 +1,173 @@
#!/bin/sh #!/bin/sh
#
# installer.sh
# Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net> # Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>
# #
# Distributed under terms of the GPLv3 license. # Distributed under terms of the GPLv3 license.
#
python=$(command -v python3) { \unalias command; \unset -f command; } >/dev/null 2>&1
if [ -z "$python" ]; then tdir=''
python=$(command -v python2) cleanup() {
fi [ -n "$tdir" ] && {
if [ -z "$python" ]; then command rm -rf "$tdir"
python=$(command -v python2.7) tdir=''
fi }
if [ -z "$python" ]; then }
python=$(command -v python)
fi
if [ -z "$python" ]; then
python=python
fi
echo Using python executable: $python die() {
cleanup
printf "\033[31m%s\033[m\n\r" "$*" > /dev/stderr;
exit 1;
}
$python -c "import sys; script_launch=lambda:sys.exit('Download of installer failed!'); exec(sys.stdin.read()); script_launch()" "$@" <<'INSTALLER_HEREDOC' detect_network_tool() {
# {{{ if command -v curl 2> /dev/null > /dev/null; then
# HEREDOC_START fetch() {
#!/usr/bin/env python3 command curl -fL "$1"
# vim:fileencoding=utf-8 }
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net> fetch_quiet() {
command curl -fsSL "$1"
from __future__ import ( }
absolute_import, division, print_function, unicode_literals elif command -v wget 2> /dev/null > /dev/null; then
) fetch() {
command wget -O- "$1"
import atexit }
import json fetch_quiet() {
import os command wget --quiet -O- "$1"
import platform }
import re else
import shlex die "Neither curl nor wget available, cannot download kitty"
import shutil fi
import subprocess }
import sys
import tempfile
py3 = sys.version_info[0] > 2
is64bit = platform.architecture()[0] == '64bit'
is_macos = 'darwin' in sys.platform.lower()
is_linux_arm = is_linux_arm64 = False
if is_macos:
mac_ver = tuple(map(int, platform.mac_ver()[0].split('.')))
if mac_ver[:2] < (10, 12):
raise SystemExit('Your version of macOS is too old, at least 10.12 is required')
else:
machine = (os.uname()[4] or '').lower()
if machine.startswith('arm') or machine.startswith('aarch64'):
is_linux_arm = True
is_linux_arm64 = machine.startswith('arm64') or machine.startswith('aarch64')
try:
__file__
from_file = True
except NameError:
from_file = False
if py3:
unicode = str
raw_input = input
import urllib.request as urllib
def encode_for_subprocess(x):
return x
else:
from future_builtins import map
import urllib2 as urllib
def encode_for_subprocess(x):
if isinstance(x, unicode):
x = x.encode('utf-8')
return x
def run(*args): detect_os() {
if len(args) == 1: arch=""
args = shlex.split(args[0]) case "$(command uname)" in
args = list(map(encode_for_subprocess, args)) 'Darwin') OS="macos";;
ret = subprocess.Popen(args).wait() 'Linux')
if ret != 0: OS="linux"
raise SystemExit(ret) case "$(command uname -m)" in
x86_64) arch="x86_64";;
aarch64*) arch="arm64";;
armv8*) arch="arm64";;
i386) arch="i686";;
i686) arch="i686";;
*) die "Unknown CPU architecture $(command uname -m)";;
esac
;;
*) die "kitty binaries are not available for $(command uname)"
esac
}
expand_tilde() {
tilde_less="${1#\~/}"
[ "$1" != "$tilde_less" ] && tilde_less="$HOME/$tilde_less"
printf '%s' "$tilde_less"
}
parse_args() {
dest='~/.local'
[ "$OS" = "macos" ] && dest="/Applications"
launch='y'
installer=''
while :; do
case "$1" in
dest=*) dest="${1#*=}";;
launch=*) launch="${1#*=}";;
installer=*) installer="${1#*=}";;
"") break;;
*) die "Unrecognized command line option: $1";;
esac
shift
done
dest=$(expand_tilde "${dest}")
[ "$launch" != "y" -a "$launch" != "n" ] && die "Unrecognized command line option: launch=$launch"
dest="$dest/kitty.app"
}
class Reporter: # {{{ get_file_url() {
url="https://github.com/kovidgoyal/kitty/releases/download/$1/kitty-$2"
if [ "$OS" = "macos" ]; then
url="$url.dmg"
else
url="$url-$arch.txz"
fi
}
def __init__(self, fname): get_release_url() {
self.fname = fname release_version=$(fetch_quiet "https://sw.kovidgoyal.net/kitty/current-version.txt")
self.last_percent = 0 [ $? -ne 0 -o -z "$release_version" ] && die "Could not get kitty latest release version"
get_file_url "v$release_version" "$release_version"
def __call__(self, blocks, block_size, total_size): }
percent = (blocks*block_size)/float(total_size)
report = '\rDownloaded {:.1%} '.format(percent)
if percent - self.last_percent > 0.05:
self.last_percent = percent
print(report, end='')
sys.stdout.flush()
# }}}
def get_release_data(relname='latest'): get_nightly_url() {
print('Checking for latest release on GitHub...') get_file_url "nightly" "nightly"
req = urllib.Request( }
'https://api.github.com/repos/kovidgoyal/kitty/releases/' + relname,
headers={'Accept': 'application/vnd.github.v3+json'})
try:
res = urllib.urlopen(req).read().decode('utf-8')
except Exception as err:
raise SystemExit('Failed to contact {} with error: {}'.format(req.get_full_url(), err))
data = json.loads(res)
html_url = data['html_url'].replace('/tag/', '/download/').rstrip('/')
for asset in data.get('assets', ()):
name = asset['name']
if is_macos:
if name.endswith('.dmg'):
return html_url + '/' + name, asset['size']
else:
if name.endswith('.txz'):
if is64bit:
q = '-arm64.txz' if is_linux_arm64 else '-x86_64.txz'
if name.endswith(q):
return html_url + '/' + name, asset['size']
else:
if name.endswith('-i686.txz'):
return html_url + '/' + name, asset['size']
raise SystemExit('Failed to find the installer package on github')
get_download_url() {
installer_is_file="n"
case "$installer" in
"nightly") get_nightly_url ;;
"") get_release_url ;;
*) installer_is_file="y" ;;
esac
}
def do_download(url, size, dest): linux_install() {
print('Will download and install', os.path.basename(dest)) if [ "$installer_is_file" = "y" ]; then
reporter = Reporter(os.path.basename(dest)) command tar -C "$dest" "-xJof" "$installer"
else
printf '%s\n\n' "Downloading from: $url"
fetch "$url" | command tar -C "$dest" "-xJof" "-"
fi
}
# Get content length and check if range is supported macos_install() {
rq = urllib.urlopen(url) tdir=$(command mktemp -d "/tmp/kitty-install-XXXXXXXXXXXX")
headers = rq.info() [ "$installer_is_file" != "y" ] && {
sent_size = int(headers['content-length']) installer="$tdir/kitty.dmg"
if sent_size != size: printf '%s\n\n' "Downloading from: $url"
raise SystemExit('Failed to download from {} Content-Length ({}) != {}'.format(url, sent_size, size)) fetch "$url" > "$installer" || die "Failed to download: $url"
with open(dest, 'wb') as f: }
while f.tell() < size: command mkdir "$tdir/mp"
raw = rq.read(8192) command hdiutil attach "$installer" "-mountpoint" "$tdir/mp" || die "Failed to mount kitty.dmg"
if not raw: command ditto -v "$tdir/mp/kitty.app" "$dest"
break rc="$?"
f.write(raw) command hdiutil detach "$tdir/mp"
reporter(f.tell(), 1, size) command rm -rf "$tdir"
rq.close() tdir=''
if os.path.getsize(dest) < size: [ "$rc" != "0" ] && die "Failed to copy kitty.app from mounted dmg"
raise SystemExit('Download failed, try again later') }
print('\rDownloaded {} bytes'.format(os.path.getsize(dest)))
prepare_install_dest() {
printf "%s\n" "Installing to $dest"
command rm -rf "$dest"
command mkdir -p "$dest" || die "Failed to create the directory: $dest"
}
def clean_cache(cache, fname): exec_kitty() {
for x in os.listdir(cache): if [ "$OS" = "macos" ]; then
if fname not in x: exec "open" "$dest"
os.remove(os.path.join(cache, x)) else
exec "$dest/bin/kitty" "--detach"
fi
die "Failed to launch kitty"
}
main() {
detect_os
parse_args "$@"
detect_network_tool
get_download_url
prepare_install_dest
if [ "$OS" = "macos" ]; then
macos_install
else
linux_install
fi
[ "$launch" = "y" ] && exec_kitty
exit 0
}
def download_installer(url, size): main "$@"
fname = url.rpartition('/')[-1]
tdir = tempfile.gettempdir()
cache = os.path.join(tdir, 'kitty-installer-cache')
if not os.path.exists(cache):
os.makedirs(cache)
clean_cache(cache, fname)
dest = os.path.join(cache, fname)
if os.path.exists(dest) and os.path.getsize(dest) == size:
print('Using previously downloaded', fname)
return dest
if os.path.exists(dest):
os.remove(dest)
do_download(url, size, dest)
return dest
def macos_install(dmg, dest='/Applications', launch=True):
mp = tempfile.mkdtemp()
atexit.register(shutil.rmtree, mp)
run('hdiutil', 'attach', dmg, '-mountpoint', mp)
try:
os.chdir(mp)
app = 'kitty.app'
d = os.path.join(dest, app)
if os.path.exists(d):
shutil.rmtree(d)
dest = os.path.join(dest, app)
run('ditto', '-v', app, dest)
print('Successfully installed kitty into', dest)
if launch:
run('open', dest)
finally:
os.chdir('/')
run('hdiutil', 'detach', mp)
def linux_install(installer, dest=os.path.expanduser('~/.local'), launch=True):
dest = os.path.join(dest, 'kitty.app')
if os.path.exists(dest):
shutil.rmtree(dest)
os.makedirs(dest)
print('Extracting tarball...')
run('tar', '-C', dest, '-xJof', installer)
print('kitty successfully installed to', dest)
kitty = os.path.join(dest, 'bin', 'kitty')
print('Use', kitty, 'to run kitty')
if launch:
run(kitty, '--detach')
def main(dest=None, launch=True, installer=None):
if not dest:
if is_macos:
dest = '/Applications'
else:
dest = os.path.expanduser('~/.local')
if is_linux_arm and not is_linux_arm64:
raise SystemExit(
'You are running on a 32-bit ARM system. The kitty binaries are only'
' available for 64 bit ARM systems. You will have to build from'
' source.')
if not installer:
url, size = get_release_data()
installer = download_installer(url, size)
else:
if installer == 'nightly':
url, size = get_release_data('tags/nightly')
installer = download_installer(url, size)
else:
installer = os.path.abspath(installer)
if not os.access(installer, os.R_OK):
raise SystemExit('Could not read from: {}'.format(installer))
if is_macos:
macos_install(installer, dest=dest, launch=launch)
else:
linux_install(installer, dest=dest, launch=launch)
def script_launch():
# To test: python3 -c "import runpy; runpy.run_path('installer.py', run_name='script_launch')"
def path(x):
return os.path.expandvars(os.path.expanduser(x))
def to_bool(x):
return x.lower() in {'y', 'yes', '1', 'true'}
type_map = {x: path for x in 'dest installer'.split()}
type_map['launch'] = to_bool
kwargs = {}
for arg in sys.argv[1:]:
if arg:
m = re.match('([a-z_]+)=(.+)', arg)
if m is None:
raise SystemExit('Unrecognized command line argument: ' + arg)
k = m.group(1)
if k not in type_map:
raise SystemExit('Unrecognized command line argument: ' + arg)
kwargs[k] = type_map[k](m.group(2))
main(**kwargs)
def update_installer_wrapper():
# To run: python3 -c "import runpy; runpy.run_path('installer.py', run_name='update_wrapper')" installer.sh
with open(__file__, 'rb') as f:
src = f.read().decode('utf-8')
wrapper = sys.argv[-1]
with open(wrapper, 'r+b') as f:
raw = f.read().decode('utf-8')
nraw = re.sub(r'^# HEREDOC_START.+^# HEREDOC_END', lambda m: '# HEREDOC_START\n{}\n# HEREDOC_END'.format(src), raw, flags=re.MULTILINE | re.DOTALL)
if 'update_intaller_wrapper()' not in nraw:
raise SystemExit('regex substitute of HEREDOC failed')
f.seek(0), f.truncate()
f.write(nraw.encode('utf-8'))
if __name__ == '__main__' and from_file:
main()
elif __name__ == 'update_wrapper':
update_installer_wrapper()
elif __name__ == 'script_launch':
script_launch()
# HEREDOC_END
# }}}
INSTALLER_HEREDOC

View File

@@ -4,8 +4,8 @@ Integrations with other tools
================================ ================================
kitty provides extremely powerful interfaces such as :doc:`remote-control` and kitty provides extremely powerful interfaces such as :doc:`remote-control` and
:doc:`kittens/custom` and :doc:`kittens/icat` :doc:`kittens/custom` and :doc:`kittens/icat` that allow it to be integrated
that allow it to be integrated with other tools seamlessly. with other tools seamlessly.
Image and document viewers Image and document viewers
@@ -30,19 +30,28 @@ Display markdown files nicely formatted with images in the terminal
`ranger <https://github.com/ranger/ranger>`_ `ranger <https://github.com/ranger/ranger>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A terminal file manager, with previews of file contents powered by kitty's graphics protocol. A terminal file manager, with previews of file contents powered by kitty's
graphics protocol.
.. _tool_nnn: .. _tool_nnn:
`nnn <https://github.com/jarun/nnn/>`_ `nnn <https://github.com/jarun/nnn/>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Another terminal file manager, with previews of file contents powered by kitty's graphics protocol. Another terminal file manager, with previews of file contents powered by kitty's
graphics protocol.
.. _tool_hunter: .. _tool_hunter:
`hunter <https://github.com/rabite0/hunter>`_ `hunter <https://github.com/rabite0/hunter>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Another terminal file manager, with previews of file contents powered by kitty's graphics protocol. Another terminal file manager, with previews of file contents powered by kitty's
graphics protocol.
.. _tool_term_image:
`term-image <https://github.com/AnonymouX47/term-image>`__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Tool to browse images in a terminal using kitty's graphics protocol.
.. _tool_koneko: .. _tool_koneko:
@@ -61,8 +70,8 @@ View images in the terminal, similar to kitty's icat.
`nb <https://github.com/xwmx/nb>`_ `nb <https://github.com/xwmx/nb>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
command line and local web note-taking, bookmarking, archiving, and Command line and local web note-taking, bookmarking, archiving, and knowledge
knowledge base application that uses kitty's graphics protocol for images. base application that uses kitty's graphics protocol for images.
.. _tool_w3m: .. _tool_w3m:
@@ -75,9 +84,9 @@ images.
`timg <https://github.com/hzeller/timg>`_ `timg <https://github.com/hzeller/timg>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A terminal image and video viewer, that displays static and animated A terminal image and video viewer, that displays static and animated images or
images or plays videos. Fast multi-threaded loading, JPEG exif rotation, plays videos. Fast multi-threaded loading, JPEG exif rotation, grid view and
grid view and connecting to the webcam make it a versatile terminal utility. connecting to the webcam make it a versatile terminal utility.
System and data visualisation tools System and data visualisation tools
@@ -87,7 +96,8 @@ System and data visualisation tools
`neofetch <https://github.com/dylanaraps/neofetch>`_ `neofetch <https://github.com/dylanaraps/neofetch>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A command line system information tool that shows images using kitty's graphics protocol A command line system information tool that shows images using kitty's graphics
protocol
.. _tool_matplotlib: .. _tool_matplotlib:
@@ -105,16 +115,16 @@ Show images from Julia directly in kitty
`euporie <https://github.com/joouha/euporie>`_ `euporie <https://github.com/joouha/euporie>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A text-based user interface for running and editing Jupyter notebooks, A text-based user interface for running and editing Jupyter notebooks, powered
powered by kitty's graphics protocol for displaying plots by kitty's graphics protocol for displaying plots
.. _tool_gnuplot: .. _tool_gnuplot:
`gnuplot <http://www.gnuplot.info/>`_ `gnuplot <http://www.gnuplot.info/>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A graphing and data visualization tool that can be made to display its A graphing and data visualization tool that can be made to display its output in
output in kitty with the following bash snippet: kitty with the following bash snippet:
.. code-block:: sh .. code-block:: sh
@@ -171,26 +181,28 @@ Run applications on your system with fuzzy find inside a kitty window
Editor integration Editor integration
----------------------- -----------------------
|kitty| can be integrated into many different terminal editors to add features |kitty| can be integrated into many different terminal based text editors to add
such a split windows, previews, REPLs etc. features such a split windows, previews, REPLs etc.
.. tool_kakoune: .. tool_kakoune:
`kakoune <https://kakoune.org/>`_ `kakoune <https://kakoune.org/>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
integrates with kitty to use native kitty windows for its windows/panels and REPLs. Integrates with kitty to use native kitty windows for its windows/panels and
REPLs.
.. tool_vim_slime: .. tool_vim_slime:
`vim-slime <https://github.com/jpalardy/vim-slime#kitty>`_ `vim-slime <https://github.com/jpalardy/vim-slime#kitty>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
uses kitty remote control for a Lisp REPL. Uses kitty remote control for a Lisp REPL.
.. tool_vim_kitty_navigator: .. tool_vim_kitty_navigator:
`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_ `vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys. Allows you to navigate seamlessly between vim and kitty splits using a
consistent set of hotkeys.
.. tool_vim_test: .. tool_vim_test:
@@ -202,7 +214,7 @@ Allows easily running tests in a terminal window
`hologram.nvim <https://github.com/edluffy/hologram.nvim>`_ `hologram.nvim <https://github.com/edluffy/hologram.nvim>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Terminal image viewer for nvim Terminal image viewer for Neovim
Scrollback manipulation Scrollback manipulation
@@ -228,21 +240,21 @@ Miscellaneous
`kitty-smart-tab <https://github.com/yurikhan/kitty-smart-tab>`_ `kitty-smart-tab <https://github.com/yurikhan/kitty-smart-tab>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
use keys to either control tabs or pass them onto running applications if Use keys to either control tabs or pass them onto running applications if no
no tabs are present tabs are present
.. tool_kitty_smart_scroll: .. tool_kitty_smart_scroll:
`kitty-smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_ `kitty-smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
use keys to either scroll or pass them onto running applications if Use keys to either scroll or pass them onto running applications if no
no scrollback buffer is present scrollback buffer is present
.. tool_kitti3: .. tool_kitti3:
`kitti3 <https://github.com/LandingEllipse/kitti3>`_ `kitti3 <https://github.com/LandingEllipse/kitti3>`_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
allow using kitty as a drop-down terminal under the i3 window manager Allow using kitty as a drop-down terminal under the i3 window manager
.. tool_weechat_hints: .. tool_weechat_hints:

View File

@@ -35,9 +35,14 @@ inside the kitty terminal to report key events.
In addition to kitty, this protocol is also implemented in: In addition to kitty, this protocol is also implemented in:
* The `foot terminal <https://codeberg.org/dnkl/foot/issues/319>`__ * The `foot terminal <https://codeberg.org/dnkl/foot/issues/319>`__
* The `WezTerm terminal <https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html>`__
* The `notcurses library * The `notcurses library
<https://github.com/dankamongmen/notcurses/issues/2131>`__ <https://github.com/dankamongmen/notcurses/issues/2131>`__
* The `crossterm library
<https://github.com/crossterm-rs/crossterm/pull/688>`__
* The `neovim text editor <https://github.com/neovim/neovim/pull/18181>`__
* The `kakoune text editor <https://github.com/mawww/kakoune/issues/4103>`__ * The `kakoune text editor <https://github.com/mawww/kakoune/issues/4103>`__
* The `dte text editor <https://gitlab.com/craigbarnes/dte/-/issues/138>`__
.. versionadded:: 0.20.0 .. versionadded:: 0.20.0
@@ -376,7 +381,7 @@ An application can query the terminal for support of this protocol by sending
the escape code querying for the :ref:`current progressive enhancement the escape code querying for the :ref:`current progressive enhancement
<progressive_enhancement>` status <progressive_enhancement>` status
followed by request for the `primary device attributes followed by request for the `primary device attributes
<https://vt100.net/docs/vt510-rm/DA1.html>`. If an answer for the device <https://vt100.net/docs/vt510-rm/DA1.html>`__. If an answer for the device
attributes is received without getting back an answer for the progressive attributes is received without getting back an answer for the progressive
enhancement the terminal does not support this protocol. enhancement the terminal does not support this protocol.

View File

@@ -3,18 +3,21 @@ broadcast
*Type text in all kitty windows simultaneously* *Type text in all kitty windows simultaneously*
The ``broadcast`` kitten can be used to type text simultaneously in The ``broadcast`` kitten can be used to type text simultaneously in all
all kitty windows (or a subset as desired). :term:`kitty windows <window>` (or a subset as desired).
To use it, simply create a mapping in :file:`kitty.conf` such as:: To use it, simply create a mapping in :file:`kitty.conf` such as::
map F1 launch --allow-remote-control kitty +kitten broadcast map f1 launch --allow-remote-control kitty +kitten broadcast
Then press the :kbd:`F1` key and whatever you type in the newly created widow Then press the :kbd:`F1` key and whatever you type in the newly created window
will be sent to all kitty windows. will be sent to all kitty windows.
You can use the options described below to control which windows You can use the options described below to control which windows are selected.
are selected.
For example, only broadcast to other windows in the current tab::
map f1 launch --allow-remote-control kitty +kitten broadcast --match-tab state:focused
.. program:: kitty +kitten broadcast .. program:: kitty +kitten broadcast

View File

@@ -1,21 +1,20 @@
Custom kittens Custom kittens
================= =================
You can easily create your own kittens to extend kitty. They are just You can easily create your own kittens to extend kitty. They are just terminal
terminal programs written in Python. When launching a kitten, kitty will programs written in Python. When launching a kitten, kitty will open an overlay
open an overlay window over the current window and optionally pass the window over the current window and optionally pass the contents of the current
contents of the current window/scrollback to the kitten over its :file:`STDIN`. window/scrollback to the kitten over its :file:`STDIN`. The kitten can then
The kitten can then perform whatever actions it likes, just as a normal perform whatever actions it likes, just as a normal terminal program. After
terminal program. After execution of the kitten is complete, it has access execution of the kitten is complete, it has access to the running kitty instance
to the running kitty instance so it can perform arbitrary actions so it can perform arbitrary actions such as closing windows, pasting text, etc.
such as closing windows, pasting text, etc.
Let's see a simple example of creating a kitten. It will ask the user for some Let's see a simple example of creating a kitten. It will ask the user for some
input and paste it into the terminal window. input and paste it into the terminal window.
Create a file in the kitty config folder, :file:`~/.config/kitty/mykitten.py` Create a file in the kitty config directory, :file:`~/.config/kitty/mykitten.py`
(you might need to adjust the path to wherever the kitty config folder is on (you might need to adjust the path to wherever the :ref:`kitty config directory
your machine). <confloc>` is on your machine).
.. code-block:: python .. code-block:: python
@@ -35,7 +34,7 @@ your machine).
# get the kitty window into which to paste answer # get the kitty window into which to paste answer
w = boss.window_id_map.get(target_window_id) w = boss.window_id_map.get(target_window_id)
if w is not None: if w is not None:
w.paste(answer) w.paste_text(answer)
Now in :file:`kitty.conf` add the lines:: Now in :file:`kitty.conf` add the lines::
@@ -43,11 +42,30 @@ Now in :file:`kitty.conf` add the lines::
map ctrl+k kitten mykitten.py map ctrl+k kitten mykitten.py
Start kitty and press :kbd:`ctrl+k` and you should see the kitten running. Start kitty and press :kbd:`Ctrl+K` and you should see the kitten running.
The best way to develop your own kittens is to modify one of the built in The best way to develop your own kittens is to modify one of the built-in
kittens. Look in the kittens sub-directory of the kitty source code for those. kittens. Look in the `kittens sub-directory
Or see below for a list of :ref:`third-party kittens <external_kittens>`, <https://github.com/kovidgoyal/kitty/tree/master/kittens>`__ of the kitty source
that other kitty users have created. code for those. Or see below for a list of :ref:`third-party kittens
<external_kittens>`, that other kitty users have created.
kitty API to use with kittens
-------------------------------
Kittens have full access to internal kitty APIs. However these are neither
entirely stable nor documented. You can instead use the kitty
:doc:`Remote control API </remote-control>`. Simply call
:code:`boss.call_remote_control()`, with the same arguments you
would pass to ``kitty @``. For example:
.. code-block:: python
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:
boss.call_remote_control(w, ('send-text', 'hello world'))
Passing arguments to kittens Passing arguments to kittens
@@ -60,36 +78,40 @@ You can pass arguments to kittens by defining them in the map directive in
These will be available as the ``args`` parameter in the ``main()`` and These will be available as the ``args`` parameter in the ``main()`` and
``handle_result()`` functions. Note also that the current working directory ``handle_result()`` functions. Note also that the current working directory
of the kitten is set to the working directory of whatever program is of the kitten is set to the working directory of whatever program is running in
running in the active kitty window. The special argument ``@selection`` the active kitty window. The special argument ``@selection`` is replaced by the
is replaced by the currently selected text in the active kitty window. currently selected text in the active kitty window.
Passing the contents of the screen to the kitten Passing the contents of the screen to the kitten
--------------------------------------------------- ---------------------------------------------------
If you would like your kitten to have access to the contents of the screen If you would like your kitten to have access to the contents of the screen
and/or the scrollback buffer, you just need to add an annotation to the ``handle_result()`` and/or the scrollback buffer, you just need to add an annotation to the
function, telling kitty what kind of input your kitten would like. For example: ``handle_result()`` function, telling kitty what kind of input your kitten would
like. For example:
.. code-block:: py .. code-block:: py
from typing import List
from kitty.boss import Boss
# in main, STDIN is for the kitten process and will contain # in main, STDIN is for the kitten process and will contain
# the contents of the screen # the contents of the screen
def main(args): def main(args: List[str]) -> str:
return sys.stdin.read() return sys.stdin.read()
# in handle_result, STDIN is for the kitty process itself, rather # in handle_result, STDIN is for the kitty process itself, rather
# than the kitten process and should not be read from. # than the kitten process and should not be read from.
from kittens.tui.handler import result_handler from kittens.tui.handler import result_handler
@result_handler(type_of_input='text') @result_handler(type_of_input='text')
def handle_result(args, stdin_data, target_window_id, boss): def handle_result(args: List[str], stdin_data: str, target_window_id: int, boss: Boss) -> None:
pass pass
This will send the plain text of the active window to the kitten's This will send the plain text of the active window to the kitten's
:file:`STDIN`. There are many other types of input you can ask for, :file:`STDIN`. There are many other types of input you can ask for, described in
described in the table below: the table below:
.. table:: Types of input to kittens .. table:: Types of input to kittens
:align: left :align: left
@@ -121,31 +143,35 @@ and ``first_output`` gives the output of the first command currently on screen.
These can also be combined with ``screen`` and ``ansi`` for formatting. These can also be combined with ``screen`` and ``ansi`` for formatting.
.. note:: .. note::
For the types based on the output of a command, For the types based on the output of a command, :ref:`shell_integration` is
:ref:`shell_integration` is required. required.
Using kittens to script kitty, without any terminal UI Using kittens to script kitty, without any terminal UI
----------------------------------------------------------- -----------------------------------------------------------
If you would like your kitten to script kitty, without bothering to write a If you would like your kitten to script kitty, without bothering to write a
terminal program, you can tell the kittens system to run the terminal program, you can tell the kittens system to run the ``handle_result()``
``handle_result()`` function without first running the ``main()`` function. function without first running the ``main()`` function.
For example, here is a kitten that "zooms/unzooms" the current terminal window For example, here is a kitten that "zooms in/zooms out" the current terminal
by switching to the stack layout or back to the previous layout. This is window by switching to the stack layout or back to the previous layout. This is
equivalent to the builtin :ac:`toggle_layout` action. equivalent to the builtin :ac:`toggle_layout` action.
Create a file in the kitty config folder, :file:`~/.config/kitty/zoom_toggle.py` Create a Python file in the :ref:`kitty config directory <confloc>`,
:file:`~/.config/kitty/zoom_toggle.py`
.. code-block:: py .. code-block:: py
def main(args): from typing import List
from kitty.boss import Boss
def main(args: List[str]) -> str:
pass pass
from kittens.tui.handler import result_handler from kittens.tui.handler import result_handler
@result_handler(no_ui=True) @result_handler(no_ui=True)
def handle_result(args, answer, target_window_id, boss): def handle_result(args: List[str], answer: str, target_window_id: int, boss: Boss) -> None:
tab = boss.active_tab tab = boss.active_tab
if tab is not None: if tab is not None:
if tab.current_layout.name == 'stack': if tab.current_layout.name == 'stack':
@@ -154,7 +180,7 @@ Create a file in the kitty config folder, :file:`~/.config/kitty/zoom_toggle.py`
tab.goto_layout('stack') tab.goto_layout('stack')
Now in kitty.conf add:: Now in :file:`kitty.conf` add::
map f11 kitten zoom_toggle.py map f11 kitten zoom_toggle.py
@@ -165,7 +191,7 @@ layout, by simply adding the line::
boss.toggle_fullscreen() boss.toggle_fullscreen()
To the ``handle_result()`` function, above. to the ``handle_result()`` function, above.
.. _send_mouse_event: .. _send_mouse_event:
@@ -173,7 +199,7 @@ To the ``handle_result()`` function, above.
Sending mouse events Sending mouse events
-------------------- --------------------
If the program running in a window is receiving mouse events you can simulate If the program running in a window is receiving mouse events, you can simulate
those using:: those using::
from kitty.fast_data_types import send_mouse_event from kitty.fast_data_types import send_mouse_event
@@ -200,15 +226,15 @@ that type, and will return ``True`` if it sent the event, and ``False`` if not.
Debugging kittens Debugging kittens
-------------------- --------------------
The part of the kitten that runs in ``main()`` is just a normal program and The part of the kitten that runs in ``main()`` is just a normal program and the
the output of print statements will be visible in the kitten window. Or output of print statements will be visible in the kitten window. Or alternately,
alternately, you can use:: you can use::
from kittens.tui.loop import debug from kittens.tui.loop import debug
debug('whatever') debug('whatever')
The ``debug()`` function is just like ``print()`` except that the output The ``debug()`` function is just like ``print()`` except that the output will
will appear in the ``STDOUT`` of the kitty process inside which the kitten is appear in the ``STDOUT`` of the kitty process inside which the kitten is
running. running.
The ``handle_result()`` part of the kitten runs inside the kitty process. The ``handle_result()`` part of the kitten runs inside the kitty process.
@@ -216,12 +242,13 @@ The output of print statements will go to the ``STDOUT`` of the kitty process.
So if you run kitty from another kitty instance, the output will be visible So if you run kitty from another kitty instance, the output will be visible
in the first kitty instance. in the first kitty instance.
Adding options to kittens Adding options to kittens
---------------------------- ----------------------------
If you would like to use kitty's config framework to make your kittens If you would like to use kitty's config framework to make your kittens
configurable, you will need some boilerplate. In the directory configurable, you will need some boilerplate. Put the following files in the
of your kitten make the following files. directory of your kitten.
:file:`kitten_options_definition.py` :file:`kitten_options_definition.py`
@@ -311,8 +338,9 @@ You can parse and read the options in your kitten using the following code:
opts.config_overrides = overrides opts.config_overrides = overrides
return opts return opts
See the code for the builtin diff kitten for examples of creating more options See `the code <https://github.com/kovidgoyal/kitty/tree/master/kittens/diff>`__
and keyboard shortcuts. for the builtin :doc:`diff kitten </kittens/diff>` for examples of creating more
options and keyboard shortcuts.
.. _external_kittens: .. _external_kittens:
@@ -320,7 +348,8 @@ Kittens created by kitty users
--------------------------------------------- ---------------------------------------------
`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_ `vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
Allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys. Allows you to navigate seamlessly between vim and kitty splits using a
consistent set of hotkeys.
`smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_ `smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
Makes the kitty scroll bindings work in full screen applications Makes the kitty scroll bindings work in full screen applications

View File

@@ -12,8 +12,8 @@ Major Features
* Displays diffs side-by-side in the kitty terminal * Displays diffs side-by-side in the kitty terminal
* Does syntax highlighting of the displayed diffs, asynchronously, for maximum * Does syntax highlighting of the displayed diffs, asynchronously, for
speed maximum speed
* Displays images as well as text diffs, even over SSH * Displays images as well as text diffs, even over SSH
@@ -31,11 +31,11 @@ Major Features
Installation Installation
--------------- ---------------
Simply :ref:`install kitty <quickstart>`. You also need Simply :ref:`install kitty <quickstart>`. You also need to have either the `git
to have either the `git <https://git-scm.com/>`_ program or the ``diff`` program <https://git-scm.com/>`__ program or the :program:`diff` program installed.
installed. Additionally, for syntax highlighting to work, Additionally, for syntax highlighting to work, `pygments
`pygments <https://pygments.org/>`_ must be installed (note that pygments is <https://pygments.org/>`__ must be installed (note that pygments is included in
included in the official kitty binary builds). the official kitty binary builds).
Usage Usage
@@ -45,9 +45,10 @@ In the kitty terminal, run::
kitty +kitten diff file1 file2 kitty +kitten diff file1 file2
to see the diff between file1 and file2. to see the diff between :file:`file1` and :file:`file2`.
Create an alias in your shell's startup file to shorten the command, for example: Create an alias in your shell's startup file to shorten the command, for
example:
.. code-block:: sh .. code-block:: sh
@@ -67,20 +68,20 @@ Keyboard controls
========================= =========================== ========================= ===========================
Action Shortcut Action Shortcut
========================= =========================== ========================= ===========================
Quit :kbd:`q`, :kbd:`ctrl+c`, :kbd:`Esc` Quit :kbd:`Q`, :kbd:`Ctrl+C`, :kbd:`Esc`
Scroll line up :kbd:`k`, :kbd:`Up` Scroll line up :kbd:`K`, :kbd:`Up`
Scroll line down :kbd:`j`, :kbd:`Down` Scroll line down :kbd:`J`, :kbd:`Down`
Scroll page up :kbd:`PgUp` Scroll page up :kbd:`PgUp`
Scroll page down :kbd:`PgDn` Scroll page down :kbd:`PgDn`
Scroll to top :kbd:`Home` Scroll to top :kbd:`Home`
Scroll to bottom :kbd:`End` Scroll to bottom :kbd:`End`
Scroll to next page :kbd:`Space`, :kbd:`PgDn` Scroll to next page :kbd:`Space`, :kbd:`PgDn`
Scroll to previous page :kbd:`PgUp` Scroll to previous page :kbd:`PgUp`
Scroll to next change :kbd:`n` Scroll to next change :kbd:`N`
Scroll to previous change :kbd:`p` Scroll to previous change :kbd:`P`
Increase lines of context :kbd:`+` Increase lines of context :kbd:`+`
Decrease lines of context :kbd:`-` Decrease lines of context :kbd:`-`
All lines of context :kbd:`a` All lines of context :kbd:`A`
Restore default context :kbd:`=` Restore default context :kbd:`=`
Search forwards :kbd:`/` Search forwards :kbd:`/`
Search backwards :kbd:`?` Search backwards :kbd:`?`
@@ -93,7 +94,7 @@ Scroll to previous match :kbd:`<`, :kbd:`,`
Integrating with git Integrating with git
----------------------- -----------------------
Add the following to `~/.gitconfig`: Add the following to :file:`~/.gitconfig`:
.. code-block:: ini .. code-block:: ini
@@ -119,24 +120,23 @@ Why does this work only in kitty?
---------------------------------------- ----------------------------------------
The diff kitten makes use of various features that are :doc:`kitty only The diff kitten makes use of various features that are :doc:`kitty only
</protocol-extensions>`, such as the :doc:`kitty graphics protocol </protocol-extensions>`, such as the :doc:`kitty graphics protocol
</graphics-protocol>`, the :doc:`extended keyboard protocol </graphics-protocol>`, the :doc:`extended keyboard protocol
</keyboard-protocol>`, etc. It also leverages terminal program </keyboard-protocol>`, etc. It also leverages terminal program infrastructure
infrastructure I created for all of kitty's other kittens to reduce the amount I created for all of kitty's other kittens to reduce the amount of code needed
of code needed (the entire implementation is under 2000 lines of code). (the entire implementation is under 2000 lines of code).
And fundamentally, it's kitty only because I wrote it for myself, and I am And fundamentally, it's kitty only because I wrote it for myself, and I am
highly unlikely to use any other terminals :) highly unlikely to use any other terminals :)
Configuration Configuration
------------------------ ------------------------
You can configure the colors used, keyboard shortcuts, the diff implementation, You can configure the colors used, keyboard shortcuts, the diff implementation,
the default lines of context, etc. by creating a :file:`diff.conf` file in the default lines of context, etc. by creating a :file:`diff.conf` file in your
your :ref:`kitty config folder <confloc>`. See below for the supported :ref:`kitty config folder <confloc>`. See below for the supported configuration
configuration directives. directives.
.. include:: /generated/conf-kitten-diff.rst .. include:: /generated/conf-kitten-diff.rst
@@ -145,7 +145,6 @@ configuration directives.
.. include:: /generated/cli-kitten-diff.rst .. include:: /generated/cli-kitten-diff.rst
Sample diff.conf Sample diff.conf
----------------- -----------------

View File

@@ -1,9 +1,9 @@
Hints Hints
========== ==========
|kitty| has a *hints mode* to select and act on arbitrary text snippets currently |kitty| has a *hints mode* to select and act on arbitrary text snippets
visible on the screen. For example, you can press :sc:`open_url` currently visible on the screen. For example, you can press :sc:`open_url`
to choose any URL visible on the screen and then open it using your system to choose any URL visible on the screen and then open it using your default web
browser. browser.
.. figure:: ../screenshots/hints_mode.png .. figure:: ../screenshots/hints_mode.png
@@ -13,25 +13,29 @@ browser.
URL hints mode URL hints mode
Similarly, you can press :sc:`insert_selected_path` to Similarly, you can press :sc:`insert_selected_path` to select anything that
select anything that looks like a path or filename and then insert it into the looks like a path or filename and then insert it into the terminal, very useful
terminal, very useful for picking files from the output of a ``git`` or ``ls`` command and for picking files from the output of a :program:`git` or :program:`ls` command
adding them to the command line for the next command. and adding them to the command line for the next command.
You can also press :sc:`goto_file_line` to select anything that looks You can also press :sc:`goto_file_line` to select anything that looks like a
like a path or filename followed by a colon and a line number and open path or filename followed by a colon and a line number and open the file in
the file in vim at the specified line number. The patterns and editor :program:`vim` at the specified line number. The patterns and editor to be used
to be used can be modified using options passed to the kitten. For example:: can be modified using options passed to the kitten. For example::
map ctrl+g kitten hints --type=linenum --linenum-action=tab nvim +{line} {path} map ctrl+g kitten hints --type=linenum --linenum-action=tab nvim +{line} {path}
will open the selected file in a new tab inside neovim when you press will open the selected file in a new tab inside `Neovim <https://neovim.io/>`__
:kbd:`ctrl+g`. when you press :kbd:`Ctrl+G`.
Pressing :sc:`open_selected_hyperlink` will open hyperlinks, i.e. a URL Pressing :sc:`open_selected_hyperlink` will open :term:`hyperlinks`, i.e. a URL
that has been marked as such by the program running in the terminal, that has been marked as such by the program running in the terminal,
for example, by ``ls --hyperlink=auto``. You can also :doc:`customize what actions are for example, by ``ls --hyperlink=auto``. If :program:`ls` comes with your OS
taken for different types of URLs <../open_actions>`. does not support hyperlink, you may need to install `GNU Coreutils
<https://www.gnu.org/software/coreutils/>`__.
You can also :doc:`customize what actions are taken for different types of URLs
<../open_actions>`.
.. note:: If there are more hints than letters, hints will use multiple .. note:: If there are more hints than letters, hints will use multiple
letters. In this case, when you press the first letter, only hints letters. In this case, when you press the first letter, only hints
@@ -48,13 +52,12 @@ snippets. See :sc:`insert_selected_path <insert_selected_path>` for examples.
Completely customizing the matching and actions of the kitten Completely customizing the matching and actions of the kitten
--------------------------------------------------------------- ---------------------------------------------------------------
The hints kitten supports writing simple python scripts that can be used to The hints kitten supports writing simple Python scripts that can be used to
completely customize how it finds matches and what happens when a match is completely customize how it finds matches and what happens when a match is
selected. This allows the hints kitten to provide the user interface, while selected. This allows the hints kitten to provide the user interface, while you
you can provide the logic for finding matches and performing actions on them. can provide the logic for finding matches and performing actions on them. This
This is best illustrated with an example. Create the file is best illustrated with an example. Create the file :file:`custom-hints.py` in
:file:`custom-hints.py` in the kitty config directory with the following the :ref:`kitty config directory <confloc>` with the following contents:
contents:
.. code-block:: python .. code-block:: python
@@ -98,9 +101,10 @@ look it up in the Google dictionary.
.. note:: .. note::
To avoid having to specify the same command line options on every invocation, To avoid having to specify the same command line options on every
you can use the :opt:`action_alias` option in :file:`kitty.conf`, creating aliases invocation, you can use the :opt:`action_alias` option in
that have common sets of options. For example:: :file:`kitty.conf`, creating aliases that have common sets of options.
For example::
action_alias myhints kitten hints --alphabet qfjdkslaureitywovmcxzpq1234567890 action_alias myhints kitten hints --alphabet qfjdkslaureitywovmcxzpq1234567890
map f1 myhints --customize-processing custom-hints.py map f1 myhints --customize-processing custom-hints.py

View File

@@ -1,11 +1,10 @@
Hyperlinked grep Hyperlinked grep
================= =================
This kitten allows you to search your files using `ripgrep This kitten allows you to search your files using `ripgrep
<https://github.com/BurntSushi/ripgrep>`_ and open the results <https://github.com/BurntSushi/ripgrep>`__ and open the results directly in your
directly in your favorite editor in the terminal, at the line containing favorite editor in the terminal, at the line containing the search result,
the search result, simply by clicking on the result you want. simply by clicking on the result you want.
.. versionadded:: 0.19.0 .. versionadded:: 0.19.0
@@ -25,19 +24,19 @@ following contents:
mime text/* mime text/*
action launch --type=overlay ${EDITOR} ${FILE_PATH} action launch --type=overlay ${EDITOR} ${FILE_PATH}
Now, run a search with:: Now, run a search with::
kitty +kitten hyperlinked_grep something kitty +kitten hyperlinked_grep something
Hold down the :kbd:`ctrl+shift` keys and click on any of the Hold down the :kbd:`Ctrl+Shift` keys and click on any of the result lines, to
result lines, to open the file in vim at the matching line. If open the file in :program:`vim` at the matching line. If you use some editor
you use some editor other than vim, you should adjust the other than :program:`vim`, you should adjust the :file:`open-actions.conf` file
:file:`open-actions.conf` file accordingly. accordingly.
Finally, add an alias to your shell's rc files to invoke the kitten as ``hg``:: Finally, add an alias to your shell's rc files to invoke the kitten as
:command:`hg`::
alias hg='kitty +kitten hyperlinked_grep' alias hg="kitty +kitten hyperlinked_grep"
You can now run searches with:: You can now run searches with::
@@ -45,13 +44,13 @@ You can now run searches with::
hg some-search-term hg some-search-term
If you want to enable completion, for the kitten, you can delegate completion If you want to enable completion, for the kitten, you can delegate completion
to rg. How to do that varies based on the shell: to :program:`rg`. How to do that varies based on the shell:
.. tab:: zsh .. tab:: zsh
Instead of using an alias create a simple wrapper script named Instead of using an alias, create a simple wrapper script named
:file:`hg` somewhere in your ``PATH``: :program:`hg` somewhere in your :envvar:`PATH`:
.. code-block:: sh .. code-block:: sh
@@ -64,22 +63,33 @@ to rg. How to do that varies based on the shell:
.. tab:: fish .. tab:: fish
You can combine both the aliasing/wrapping and pointing fish You can combine both the aliasing/wrapping and pointing fish to ripgrep's
to rg's autocompletion with a fish "wrapper" function in your :file:`config.fish`: autocompletion with a fish wrapper function in your :file:`config.fish`
or :file:`~/.config/fish/functions/hg.fish`:
.. code-block:: sh .. code-block:: fish
function hg --wraps rg; kitty +kitten hyperlinked_grep $argv; end function hg --wraps rg; kitty +kitten hyperlinked_grep $argv; end
To learn more about kitty's powerful framework for customizing URL click To learn more about kitty's powerful framework for customizing URL click
actions, :doc:`see here </open_actions>`. actions, see :doc:`here </open_actions>`.
By default, this kitten adds hyperlinks for several parts of ripgrep output:
the per-file header, match context lines, and match lines. You can control
which items are linked with a :command:`--kitten hyperlink` flag. For example,
:command:`--kitten hyperlink=matching_lines` will only add hyperlinks to the
match lines. :command:`--kitten hyperlink=file_headers,context_lines` will link
file headers and context lines but not match lines. :command:`--kitten
hyperlink=none` will cause the command line to be passed to directly to
:command:`rg` so no hyperlinking will be performed. :command:`--kitten
hyperlink` may be specified multiple times.
Hopefully, someday this functionality will make it into some `upstream grep Hopefully, someday this functionality will make it into some `upstream grep
<https://github.com/BurntSushi/ripgrep/issues/665>`_ <https://github.com/BurntSushi/ripgrep/issues/665>`__ program directly removing
program directly removing the need for this kitten. the need for this kitten.
.. note:: .. note::
While you can pass any of ripgrep's comand line options to the kitten and While you can pass any of ripgrep's comand line options to the kitten and
they will be forwarded to rg, do not use options that change the output they will be forwarded to :program:`rg`, do not use options that change the
formatting as the kitten works by parsing the output from ripgrep. output formatting as the kitten works by parsing the output from ripgrep.

View File

@@ -9,8 +9,8 @@ terminal. Using it is as simple as::
kitty +kitten icat image.jpeg kitty +kitten icat image.jpeg
It supports all image types supported by `ImageMagick It supports all image types supported by `ImageMagick
<https://www.imagemagick.org>`_. It even works over SSH. For details, see <https://www.imagemagick.org>`__. It even works over SSH. For details, see the
the :doc:`kitty graphics protocol </graphics-protocol>`. :doc:`kitty graphics protocol </graphics-protocol>`.
You might want to create an alias in your shell's configuration files:: You might want to create an alias in your shell's configuration files::
@@ -20,14 +20,14 @@ Then you can simply use ``icat image.png`` to view images.
.. note:: .. note::
`ImageMagick <https://www.imagemagick.org>`_ must be installed for ``icat`` to `ImageMagick <https://www.imagemagick.org>`__ must be installed for icat
work. kitten to work.
.. note:: .. note::
kitty's image display protocol may not work when used within a terminal kitty's image display protocol may not work when used within a terminal
multiplexer such as ``screen`` or ``tmux``, depending on whether the multiplexer such as :program:`screen` or :program:`tmux`, depending on
multiplexer has added support for it or not. whether the multiplexer has added support for it or not.
.. program:: kitty +kitten icat .. program:: kitty +kitten icat
@@ -35,18 +35,19 @@ Then you can simply use ``icat image.png`` to view images.
The ``icat`` kitten has various command line arguments to allow it to be used The ``icat`` kitten has various command line arguments to allow it to be used
from inside other programs to display images. In particular, :option:`--place`, from inside other programs to display images. In particular, :option:`--place`,
:option:`--detect-support`, :option:`--silent` and :option:`--print-window-size`. :option:`--detect-support`, :option:`--silent` and
:option:`--print-window-size`.
If you are trying to integrate icat into a complex program like a file If you are trying to integrate icat into a complex program like a file manager
manager or editor, there are a few things to keep in mind. icat works by or editor, there are a few things to keep in mind. icat works by communicating
communicating over the TTY device, it both writes to and reads from the TTY. over the TTY device, it both writes to and reads from the TTY. So it is
So it is imperative that while it is running the host program does not do imperative that while it is running the host program does not do any TTY I/O.
any TTY I/O. Any key presses or other input from the user on the TTY device Any key presses or other input from the user on the TTY device will be
will be discarded. At a minimum, you should use the :option:`--silent` and discarded. At a minimum, you should use the :option:`--silent` and
:option:`--transfer-mode` command line arguments. To be :option:`--transfer-mode` command line arguments. To be really robust you should
really robust you should consider writing proper support for the consider writing proper support for the :doc:`kitty graphics protocol
:doc:`../graphics-protocol` in the program instead. Nowadays there are many </graphics-protocol>` in the program instead. Nowadays there are many libraries
libraries that have support for it. that have support for it.
.. include:: /generated/cli-kitten-icat.rst .. include:: /generated/cli-kitten-icat.rst

View File

@@ -4,8 +4,8 @@ Draw a GPU accelerated dock panel on your desktop
.. highlight:: sh .. highlight:: sh
You can use this kitten to draw a GPU accelerated panel on the edge You can use this kitten to draw a GPU accelerated panel on the edge of your
of your screen, that shows the output from an arbitrary terminal program. screen, that shows the output from an arbitrary terminal program.
It is useful for showing status information or notifications on your desktop It is useful for showing status information or notifications on your desktop
using terminal programs instead of GUI toolkits. using terminal programs instead of GUI toolkits.
@@ -32,8 +32,8 @@ Using this kitten is simple, for example::
kitty +kitten panel sh -c 'printf "\n\n\nHello, world."; sleep 5s' kitty +kitten panel sh -c 'printf "\n\n\nHello, world."; sleep 5s'
This will show ``Hello, world.`` at the top edge of your screen for five This will show ``Hello, world.`` at the top edge of your screen for five
seconds. Here the terminal program we are running is ``sh`` with a script to seconds. Here the terminal program we are running is :program:`sh` with a script
print out ``Hello, world!``. You can make the terminal program as complex as to print out ``Hello, world!``. You can make the terminal program as complex as
you like, as demonstrated in the screenshot above. you like, as demonstrated in the screenshot above.

View File

@@ -1,18 +1,18 @@
Query terminal Query terminal
================= =================
Used to query kitty from terminal programs about version, values of various This kitten is used to query |kitty| from terminal programs about version, values
runtime options controlling its features, etc. of various runtime options controlling its features, etc.
The querying is done using the (*semi*) standard XTGETTCAP escape sequence The querying is done using the (*semi*) standard XTGETTCAP escape sequence
pioneered by XTerm, so it works over SSH as well. The downside is that it pioneered by xterm, so it works over SSH as well. The downside is that it is
is slow, since it requires a roundtrip to the terminal emulator and back. slow, since it requires a roundtrip to the terminal emulator and back.
If you want to do some of the same querying in your terminal program without If you want to do some of the same querying in your terminal program without
depending on the kitten, you can do so, by processing the same escape codes. depending on the kitten, you can do so, by processing the same escape codes.
Search `this page <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html>`_ Search `this page <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html>`__
for *XTGETTCAP* to see the syntax for the escape code and read the source for *XTGETTCAP* to see the syntax for the escape code and read the source of
of this kitten to find the values of the keys for the various queries. this kitten to find the values of the keys for the various queries.
.. include:: ../generated/cli-kitten-query_terminal.rst .. include:: ../generated/cli-kitten-query_terminal.rst

View File

@@ -1,12 +1,12 @@
Remote files Remote files
============== ==============
|kitty| has the ability to easily *Edit*, *Open* or *Download* files |kitty| has the ability to easily *Edit*, *Open* or *Download* files from a
from a computer into which you are SSHed. In your SSH session run:: computer into which you are SSHed. In your SSH session run::
ls --hyperlink=auto ls --hyperlink=auto
Then hold down :kbd:`ctrl+shift` and click the name of the file. Then hold down :kbd:`Ctrl+Shift` and click the name of the file.
.. figure:: ../screenshots/remote_file.png .. figure:: ../screenshots/remote_file.png
:alt: Remote file actions :alt: Remote file actions
@@ -19,23 +19,28 @@ Then hold down :kbd:`ctrl+shift` and click the name of the file.
to *Edit* it in which case kitty will download it and open it locally in your to *Edit* it in which case kitty will download it and open it locally in your
:envvar:`EDITOR`. As you make changes to the file, they are automatically :envvar:`EDITOR`. As you make changes to the file, they are automatically
transferred to the remote computer. Note that this happens without needing transferred to the remote computer. Note that this happens without needing
to install *any* special software on the server, beyond ``ls`` that supports to install *any* special software on the server, beyond :program:`ls` that
hyperlinks. supports hyperlinks.
.. seealso:: See the :ref:`edit-in-kitty <edit_file>` command
.. seealso:: See the :doc:`transfer` kitten .. seealso:: See the :doc:`transfer` kitten
.. versionadded:: 0.19.0 .. versionadded:: 0.19.0
.. note:: .. note::
Nested SSH sessions are not supported. The kitten will always try to copy For best results, use this kitten with the :doc:`ssh kitten <./ssh>`.
remote files from the first SSH host. This is because there is no way for Otherwise, nested SSH sessions are not supported. The kitten will always try to copy
remote files from the first SSH host. This is because, without the ssh
kitten, there is no way for
|kitty| to detect and follow a nested SSH session robustly. Use the |kitty| to detect and follow a nested SSH session robustly. Use the
:doc:`transfer` kitten for such situations. :doc:`transfer` kitten for such situations.
.. note:: .. note::
If you have not setup automatic password-less SSH access, then, when If you have not setup automatic password-less SSH access, and are not using
editing starts you will be asked to enter your password just once, the ssh kitten, then, when editing
thereafter the SSH connection will be re-used. starts you will be asked to enter your password just once, thereafter the SSH
connection will be re-used.
Similarly, you can choose to save the file to the local computer or download Similarly, you can choose to save the file to the local computer or download
and open it in its default file handler. and open it in its default file handler.

164
docs/kittens/ssh.rst Normal file
View File

@@ -0,0 +1,164 @@
Truly convenient SSH
=========================================
* Automatic :ref:`shell_integration` on remote hosts
* Easily :ref:`clone local shell/editor config <real_world_ssh_kitten_config>` on remote hosts
* Automatic :opt:`re-use of existing connections <kitten-ssh.share_connections>` to avoid connection setup latency
* Make kitty itself available in the remote host :opt:`on demand <kitten-ssh.remote_kitty>`
* Easily :opt:`change terminal colors <kitten-ssh.color_scheme>` when connecting to remote hosts
.. versionadded:: 0.25.0
Automatic shell integration, file transfer and reuse of connections
The ssh kitten allows you to login easily to remote hosts, and automatically
setup the environment there to be as comfortable as your local shell. You can
specify environment variables to set on the remote host and files to copy there,
making your remote experience just like your local shell. Additionally, it
automatically sets up :ref:`shell_integration` on the remote host and copies the
kitty terminfo database there.
The ssh kitten is a thin wrapper around the traditional `ssh <https://man.openbsd.org/ssh>`__
command line program and supports all the same options and arguments and configuration.
In interactive usage scenarios it is a drop in replacement for :program:`ssh`.
To try it out, simply run:
.. code-block:: sh
kitty +kitten ssh some-hostname-to-connect-to
You should end up at a shell prompt on the remote host, with shell integration
enabled. If you like it you can add an alias to it in your shell's rc files:
.. code-block:: sh
alias s="kitty +kitten ssh"
So now you can just type ``s hostname`` to connect.
If you define a mapping in :file:`kitty.conf` such as::
map f1 new_window_with_cwd
Then, pressing :kbd:`F1` will open a new window automatically logged into the
same host using the ssh kitten, at the same directory.
The ssh kitten can be configured using the :file:`~/.config/kitty/ssh.conf` file
where you can specify environment variables to set on the remote host and files
to copy from the local to the remote host. Let's see a quick example:
.. code-block:: conf
# Copy the files and directories needed to setup some common tools
copy .zshrc .vimrc .vim
# Setup some environment variables
env SOME_VAR=x
# COPIED_VAR will have the same value on the remote host as it does locally
env COPIED_VAR=_kitty_copy_env_var_
# Create some per hostname settings
hostname someserver-*
copy env-files
env SOMETHING=else
hostname someuser@somehost
copy --dest=foo/bar some-file
copy --glob some/files.*
See below for full details on the syntax and options of :file:`ssh.conf`.
Additionally, you can pass config options on the command line:
.. code-block:: sh
kitty +kitten ssh --kitten interpreter=python servername
The :code:`--kitten` argument can be specified multiple times, with directives
from :file:`ssh.conf`. These are merged with :file:`ssh.conf` as if they were
appended to the end of that file. They apply only to the host being SSHed to by
this invocation, so any :opt:`hostname <kitten-ssh.hostname>` directives are
ignored.
.. warning::
Due to limitations in the design of SSH, any typing you do before the
shell prompt appears may be lost. So ideally don't start typing till you see
the shell prompt. 😇
.. _real_world_ssh_kitten_config:
A real world example
----------------------
Suppose you often SSH into a production server, and you would like to setup
your shell and editor there using your custom settings. However, other people
could SSH in as well and you don't want to clobber their settings. Here is how
this could be achieved using the ssh kitten with :program:`zsh` and
:program:`vim` as the shell and editor, respectively:
.. code-block:: conf
# Have these settings apply to servers in my organization
hostname myserver-*
# Setup zsh to read its files from 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
copy --dest my-conf/vim .vim
copy --dest my-conf/vim/vimrc .vimrc
How it works
----------------
The ssh kitten works by having SSH transmit and execute a POSIX sh (or
:opt:`optionally <kitten-ssh.interpreter>` Python) bootstrap script on the
remote host using an :opt:`interpreter <kitten-ssh.interpreter>`. This script
reads setup data over the TTY device, which kitty sends as a Base64 encoded
compressed tarball. The script extracts it and places the :opt:`files <kitten-ssh.copy>`
and sets the :opt:`environment variables <kitten-ssh.env>` before finally
launching the :opt:`login shell <kitten-ssh.login_shell>` with :opt:`shell
integration <kitten-ssh.shell_integration>` enabled. The data is requested by
the kitten over the TTY with a random one time password. kitty reads the request
and if the password matches a password pre-stored in shared memory on the
localhost by the kitten, the transmission is allowed. If your local
`OpenSSH <https://www.openssh.com/>`__ version is >= 8.4 then the data is
transmitted instantly without any roundtrip delay.
.. note::
When connecting to BSD hosts, it is possible the bootstrap script will fail
or run slowly, because the default shells are crippled in various ways.
Your best bet is to install Python on the remote, make sure the login shell
is something POSIX sh compliant, and use :code:`python` as the
:opt:`interpreter <kitten-ssh.interpreter>` in :file:`ssh.conf`.
.. note::
This may or may not work when using terminal multiplexers, depending on
whether they passthrough the escape codes and if the values of the
environment variables :envvar:`KITTY_PID` and :envvar:`KITTY_WINDOW_ID` are
correct in the current session (they can be wrong when connecting to a tmux
session running in a different window) and the ssh kitten is run in the
currently active multiplexer window.
.. include:: /generated/conf-kitten-ssh.rst
.. _ssh_copy_command:
The copy command
--------------------
.. include:: /generated/ssh-copy.rst

View File

@@ -1,8 +1,8 @@
Changing kitty colors Changing kitty colors
======================== ========================
The themes kitten allows you to easily change color themes, from a collection The themes kitten allows you to easily change color themes, from a collection of
of almost two hundred pre-built themes available at `kitty-themes over two hundred pre-built themes available at `kitty-themes
<https://github.com/kovidgoyal/kitty-themes>`_. To use it, simply run:: <https://github.com/kovidgoyal/kitty-themes>`_. To use it, simply run::
kitty +kitten themes kitty +kitten themes
@@ -12,9 +12,9 @@ of almost two hundred pre-built themes available at `kitty-themes
:alt: The themes kitten in action :alt: The themes kitten in action
:width: 600 :width: 600
The kitten allows you to pick a theme, with live previews of the colors. You The kitten allows you to pick a theme, with live previews of the colors. You can
can choose between light and dark themes and search by theme name by just choose between light and dark themes and search by theme name by just typing a
typing a few characters from the name. few characters from the name.
The kitten maintains a list of recently used themes to allow quick switching. The kitten maintains a list of recently used themes to allow quick switching.
@@ -24,14 +24,15 @@ If you want to restore the colors to default, you can do so by choosing the
.. versionadded:: 0.23.0 .. versionadded:: 0.23.0
The themes kitten The themes kitten
How it works How it works
---------------- ----------------
A theme in kitty is just a :file:`.conf` file containing kitty settings. A theme in kitty is just a :file:`.conf` file containing kitty settings.
When you select a theme, the kitten simply copies the :file:`.conf` file When you select a theme, the kitten simply copies the :file:`.conf` file
to :file:`~/.config/kitty/current-theme.conf` and adds an include for to :file:`~/.config/kitty/current-theme.conf` and adds an include for
:file:`current-theme.conf` to :file:`kitty.conf`. It also comments out :file:`current-theme.conf` to :file:`kitty.conf`. It also comments out any
any existing color settings in :file:`kitty.conf` so they do not interfere. 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. Once that's done, the kitten sends kitty a signal to make it reload its config.
@@ -39,9 +40,9 @@ Using your own themes
----------------------- -----------------------
You can also create your own themes as :file:`.conf` files. Put them in the You can also create your own themes as :file:`.conf` files. Put them in the
:file:`themes` sub-directory of the kitty config directory, usually, :file:`themes` sub-directory of the :ref:`kitty config directory <confloc>`,
:file:`~/.config/kitty/themes` and the kitten will automatically add them to usually, :file:`~/.config/kitty/themes`. The kitten will automatically add them
the list of themes. You can use this to modify the builtin themes, by giving to the list of themes. You can use this to modify the builtin themes, by giving
the conf file the name :file:`Some theme name.conf` to override the builtin the conf file the name :file:`Some theme name.conf` to override the builtin
theme of that name. Note that after doing so you have to run the kitten and theme of that name. Note that after doing so you have to run the kitten and
choose that theme once for your changes to be applied. choose that theme once for your changes to be applied.
@@ -52,13 +53,13 @@ Contributing new themes
If you wish to contribute a new theme to the kitty theme repository, start by If you wish to contribute a new theme to the kitty theme repository, start by
going to the `kitty-themes <https://github.com/kovidgoyal/kitty-themes>`__ going to the `kitty-themes <https://github.com/kovidgoyal/kitty-themes>`__
repository. `Fork it repository. `Fork it
<https://docs.github.com/en/get-started/quickstart/fork-a-repo>`_, and use the <https://docs.github.com/en/get-started/quickstart/fork-a-repo>`__, and use the
file :download:`template.conf file :download:`template.conf
<https://github.com/kovidgoyal/kitty-themes/raw/master/template.conf>` as a <https://github.com/kovidgoyal/kitty-themes/raw/master/template.conf>` as a
template when creating your theme. Once you are satisfied with how it looks, template when creating your theme. Once you are satisfied with how it looks,
`submit a pull request `submit a pull request
<https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request>`_ <https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request>`__
to have your theme merged into the `kitty-themes to have your theme merged into the `kitty-themes
<https://github.com/kovidgoyal/kitty-themes>`__ repository, which will make it <https://github.com/kovidgoyal/kitty-themes>`__ repository, which will make it
available in this kitten automatically. available in this kitten automatically.
@@ -67,12 +68,12 @@ available in this kitten automatically.
Changing the theme non-interactively Changing the theme non-interactively
--------------------------------------- ---------------------------------------
You can specify the theme name as an argument when invoking the kitten You can specify the theme name as an argument when invoking the kitten to have
to have it change to that theme instantly. For example:: it change to that theme instantly. For example::
kitty +kitten themes --reload-in=all Dimmed Monokai kitty +kitten themes --reload-in=all Dimmed Monokai
Will change the theme to ``Dimmed Monokai`` in all running kitty Will change the theme to ``Dimmed Monokai`` in all running kitty instances. See
instances. See below for more details on non-interactive operation. below for more details on non-interactive operation.
.. include:: ../generated/cli-kitten-themes.rst .. include:: ../generated/cli-kitten-themes.rst

View File

@@ -14,16 +14,16 @@ etc. Anywhere you have a terminal device, you can transfer files.
:alt: The transfer kitten at work :alt: The transfer kitten at work
This kitten supports transferring entire directory trees, preserving soft and This kitten supports transferring entire directory trees, preserving soft and
hard links, file permissions, times, etc. It even supports the rsync_ hard links, file permissions, times, etc. It even supports the rsync_ protocol
protocol to transfer only changes to large files. to transfer only changes to large files.
.. seealso:: See the :doc:`remote_file` kitten .. seealso:: See the :doc:`remote_file` kitten
.. note:: .. note::
This kitten (which practically means kitty) must be installed on the other This kitten (which practically means kitty) must be installed on the other
machine as well. If that is not possible you can use the :doc:`remote_file` machine as well. If that is not possible you can use the :doc:`remote_file`
kitten instead. Or write your own script to use the underlying :doc:`file transfer kitten instead. Or write your own script to use the underlying
protocol </file-transfer-protocol>`. :doc:`file transfer protocol </file-transfer-protocol>`.
.. versionadded:: 0.24.0 .. versionadded:: 0.24.0
@@ -32,7 +32,8 @@ Basic usage
--------------- ---------------
In what follows, the *local computer* is the computer running this kitten and In what follows, the *local computer* is the computer running this kitten and
the *remote computer* is the computer connected to the other end of the TTY pipe. the *remote computer* is the computer connected to the other end of the TTY
pipe.
To send a file from the local computer to the remote computer, simply run:: To send a file from the local computer to the remote computer, simply run::
@@ -42,7 +43,7 @@ You will be prompted by kitty for confirmation on allowing the transfer, and if
you grant permission, the file will be copied. you grant permission, the file will be copied.
Similarly, to get a file from the remote computer to the local computer, use Similarly, to get a file from the remote computer to the local computer, use
the :option:`kitty +kitten transfer --direction` option:: the :option:`--direction <kitty +kitten transfer --direction>` option::
kitty +kitten transfer --direction=receive /path/to/remote/file /path/to/destination/on/local/computer kitty +kitten transfer --direction=receive /path/to/remote/file /path/to/destination/on/local/computer
@@ -58,25 +59,25 @@ the fact that you are copying multiple things) it is good practice to always
use a trailing slash when the destination is supposed to be a directory. use a trailing slash when the destination is supposed to be a directory.
Also, when transferring multiple files/directories it is a good idea to Also, when transferring multiple files/directories it is a good idea to
use the :option:`kitty +kitten transfer --confirm-paths` option which will give use the :option:`--confirm-paths <kitty +kitten transfer --confirm-paths>`
you an opportunity to review and confirm the files that will be touched. option which will give you an opportunity to review and confirm the files that
will be touched.
Avoiding the confirmation prompt Avoiding the confirmation prompt
------------------------------------ ------------------------------------
Normally, when you start a file transfer kitty will prompt you for Normally, when you start a file transfer kitty will prompt you for confirmation.
confirmation. This is to ensure that hostile programs running on a remote This is to ensure that hostile programs running on a remote machine cannot
machine cannot read/write files on your computer without your permission. read/write files on your computer without your permission. If the remote machine
If the remote machine is trusted and the connection between your computer is trusted and the connection between your computer and the remote machine is
and the remote machine is secure, then you can disable the confirmation prompt secure, then you can disable the confirmation prompt by:
by:
#. Setting the :opt:`file_transfer_confirmation_bypass` option to some #. Setting the :opt:`file_transfer_confirmation_bypass` option to some password.
password.
#. When invoking the kitten use the :option:`kitty +kitten transfer --permissions-bypass` #. When invoking the kitten use the :option:`--permissions-bypass
to supply the password you set in step one. <kitty +kitten transfer --permissions-bypass>` to supply the password you set
in step one.
.. warning:: Using a password to bypass confirmation means any software running .. warning:: Using a password to bypass confirmation means any software running
on the remote machine could potentially learn that password and use it to on the remote machine could potentially learn that password and use it to
@@ -89,9 +90,10 @@ Delta transfers
----------------------------------- -----------------------------------
This kitten has the ability to use the rsync_ protocol to only transfer the This kitten has the ability to use the rsync_ protocol to only transfer the
differences between files. To turn it on use the :option:`kitty +kitten differences between files. To turn it on use the :option:`--transmit-deltas
transfer --transmit-deltas` option. Note that this will actually be slower when <kitty +kitten transfer --transmit-deltas>` option. Note that this will actually
transferring small files because of round trip overhead, so use with care. be slower when transferring small files because of round trip overhead, so use
with care.
.. include:: ../generated/cli-kitten-transfer.rst .. include:: ../generated/cli-kitten-transfer.rst

View File

@@ -1,27 +1,29 @@
Unicode input Unicode input
================ ================
You can input unicode characters by name, hex code, recently used and even an editable favorites list. You can input Unicode characters by name, hex code, recently used and even an
Press :sc:`input_unicode_character` to start the unicode input widget, shown below. editable favorites list. Press :sc:`input_unicode_character` to start the
unicode input kitten, shown below.
.. figure:: ../screenshots/unicode.png .. figure:: ../screenshots/unicode.png
:alt: A screenshot of the unicode input widget :alt: A screenshot of the unicode input kitten
:align: center :align: center
:width: 100% :width: 100%
A screenshot of the unicode input widget A screenshot of the unicode input kitten
In :guilabel:`Code` mode, you enter a unicode character by typing in the hex code for the In :guilabel:`Code` mode, you enter a Unicode character by typing in the hex
character and pressing enter, for example, type in ``2716`` and press enter to get code for the character and pressing :kbd:`Enter`. For example, type in ``2716``
. You can also choose a character from the list of recently used characters by and press :kbd:`Enter` to get ````. You can also choose a character from the
typing a leading period and then the two character index and pressing Enter. list of recently used characters by typing a leading period ``.`` and then the
The up and down arrow keys can be used to choose the previous and next unicode two character index and pressing :kbd:`Enter`.
symbol respectively. The :kbd:`Up` and :kbd:`Down` arrow keys can be used to choose the previous and
next Unicode symbol respectively.
In :guilabel:`Name` mode you instead type words from the character name and use In :guilabel:`Name` mode you instead type words from the character name and use
the arrow keys/tab to select the character from the displayed matches. You can the :kbd:`ArrowKeys` / :kbd:`Tab` to select the character from the displayed
also type a space followed by a period and the index for the match if you don't matches. You can also type a space followed by a period and the index for the
like to use arrow keys. match if you don't like to use arrow keys.
You can switch between modes using either the keys :kbd:`F1` ... :kbd:`F4` or You can switch between modes using either the keys :kbd:`F1` ... :kbd:`F4` or
:kbd:`Ctrl+1` ... :kbd:`Ctrl+4` or by pressing :kbd:`Ctrl+[` and :kbd:`Ctrl+]` :kbd:`Ctrl+1` ... :kbd:`Ctrl+4` or by pressing :kbd:`Ctrl+[` and :kbd:`Ctrl+]`

View File

@@ -15,12 +15,13 @@ Extend with kittens
kittens/remote_file kittens/remote_file
kittens/hyperlinked_grep kittens/hyperlinked_grep
kittens/transfer kittens/transfer
kittens/ssh
kittens/custom kittens/custom
kittens/* kittens/*
|kitty| has a framework for easily creating terminal programs that make use of |kitty| has a framework for easily creating terminal programs that make use of
its advanced features. These programs are called kittens. They are used both its advanced features. These programs are called kittens. They are used both to
to add features to |kitty| itself and to create useful standalone programs. add features to |kitty| itself and to create useful standalone programs.
Some prominent kittens: Some prominent kittens:
:doc:`icat <kittens/icat>` :doc:`icat <kittens/icat>`
@@ -32,8 +33,8 @@ Some prominent kittens:
images images
:doc:`Unicode Input <kittens/unicode_input>` :doc:`Unicode input <kittens/unicode_input>`
Easily input arbitrary unicode characters in |kitty| by name or hex code. Easily input arbitrary Unicode characters in |kitty| by name or hex code.
:doc:`Hints <kittens/hints>` :doc:`Hints <kittens/hints>`
@@ -47,24 +48,31 @@ Some prominent kittens:
:doc:`Transfer files <kittens/transfer>` :doc:`Transfer files <kittens/transfer>`
Transfer files and directories seamlessly and easily from remote machines over your existing Transfer files and directories seamlessly and easily from remote machines
SSH sessions with a simple command. over your existing SSH sessions with a simple command.
:doc:`Hyperlinked grep <kittens/hyperlinked_grep>` :doc:`Hyperlinked grep <kittens/hyperlinked_grep>`
Search your files using `ripgrep <https://github.com/BurntSushi/ripgrep>`_ Search your files using `ripgrep <https://github.com/BurntSushi/ripgrep>`__
and open the results directly in your favorite editor in the terminal, and open the results directly in your favorite editor in the terminal,
at the line containing the search result, simply by clicking on the result you want. at the line containing the search result, simply by clicking on the result
you want.
:doc:`Broadcast <kittens/broadcast>` :doc:`Broadcast <kittens/broadcast>`
Type in one :term:`kitty window <window>` and have it broadcast to all (or a subset) of Type in one :term:`kitty window <window>` and have it broadcast to all (or a
other :term:`kitty windows <window>`. subset) of other :term:`kitty windows <window>`.
:doc:`SSH <kittens/ssh>`
SSH with automatic :ref:`shell integration <shell_integration>`, connection
re-use for low latency and easy cloning of local shell and editor
configuration to the remote host.
:doc:`Panel <kittens/panel>` :doc:`Panel <kittens/panel>`
Draw a GPU accelerated dock panel on your desktop showing the output Draw a GPU accelerated dock panel on your desktop showing the output from an
from an arbitrary terminal program. arbitrary terminal program.
:doc:`Clipboard <kittens/clipboard>` :doc:`Clipboard <kittens/clipboard>`

295
docs/kitty_at_template.py Normal file
View File

@@ -0,0 +1,295 @@
#!/usr/bin/env python
# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>
import argparse
import base64
import hashlib
import json
import os
import sys
import termios
import time
import tty
from contextlib import contextmanager
from ctypes import (
CDLL, POINTER, byref, c_char_p, c_int, c_size_t, c_void_p,
create_string_buffer
)
from ctypes.util import find_library
_plat = sys.platform.lower()
is_macos: bool = 'darwin' in _plat
def build_crypto_tools(): # {{{
class EVP_PKEY_POINTER(c_void_p):
algorithm = 0
def __del__(self):
EVP_PKEY_free(self)
@property
def public(self):
sz = c_size_t(0)
EVP_PKEY_get_raw_public_key(self, None, byref(sz))
buf = create_string_buffer(sz.value)
EVP_PKEY_get_raw_public_key(self, buf, byref(sz))
return buf.raw
def derive_secret(self, pubkey):
pubkey = EVP_PKEY_new_raw_public_key(self.algorithm, None, pubkey, len(pubkey))
ctx = EVP_PKEY_CTX_new(self, None)
EVP_PKEY_derive_init(ctx)
EVP_PKEY_derive_set_peer(ctx, pubkey)
sz = c_size_t(0)
EVP_PKEY_derive(ctx, None, byref(sz))
buf = create_string_buffer(sz.value)
EVP_PKEY_derive(ctx, buf, byref(sz))
return hashlib.sha256(buf.raw).digest()
class EVP_PKEY_CTX_POINTER(c_void_p):
def __del__(self):
EVP_PKEY_CTX_free(self)
class EVP_CIPHER_CTX_POINTER(c_void_p):
def __del__(self):
EVP_CIPHER_CTX_free(self)
class EVP_CIPHER_POINTER(c_void_p):
pass
cl = find_library('crypto')
if not cl:
raise SystemExit('Failed to find libcrypto on your system, make sure OpenSSL is installed')
crypto = CDLL(cl)
libc = CDLL(None)
def create_crypto_func(name, *argtypes, restype=c_int, int_return_ok=lambda x: x == 1):
impl = getattr(crypto, name)
impl.restype = restype
impl.argtypes = argtypes
def func(*a):
res = impl(*a)
if restype is c_int:
if not int_return_ok(res):
print('Call to', name, 'failed with return code:', res, file=sys.stderr)
abort_on_openssl_error()
elif restype is not None and issubclass(restype, c_void_p):
if res.value is None:
print('Call to', name, 'failed with NULL return', file=sys.stderr)
abort_on_openssl_error()
return res
return func
OBJ_txt2nid = create_crypto_func('OBJ_txt2nid', c_char_p, int_return_ok=bool)
EVP_PKEY_CTX_new_id = create_crypto_func('EVP_PKEY_CTX_new_id', c_int, c_void_p, restype=EVP_PKEY_CTX_POINTER)
EVP_PKEY_CTX_new = create_crypto_func('EVP_PKEY_CTX_new', EVP_PKEY_POINTER, c_void_p, restype=EVP_PKEY_CTX_POINTER)
EVP_PKEY_keygen_init = create_crypto_func('EVP_PKEY_keygen_init', EVP_PKEY_CTX_POINTER)
EVP_PKEY_keygen = create_crypto_func('EVP_PKEY_keygen', EVP_PKEY_CTX_POINTER, POINTER(EVP_PKEY_POINTER))
ERR_print_errors_fp = create_crypto_func('ERR_print_errors_fp', c_void_p, restype=None)
EVP_PKEY_free = create_crypto_func('EVP_PKEY_free', EVP_PKEY_POINTER, restype=None)
EVP_PKEY_CTX_free = create_crypto_func('EVP_PKEY_CTX_free', EVP_PKEY_CTX_POINTER, restype=None)
EVP_PKEY_get_raw_public_key = create_crypto_func('EVP_PKEY_get_raw_public_key', EVP_PKEY_POINTER, c_char_p, POINTER(c_size_t))
EVP_PKEY_new_raw_public_key = create_crypto_func('EVP_PKEY_new_raw_public_key', c_int, c_void_p, c_char_p, c_size_t, restype=EVP_PKEY_POINTER)
EVP_PKEY_derive_init = create_crypto_func('EVP_PKEY_derive_init', EVP_PKEY_CTX_POINTER)
EVP_PKEY_derive_set_peer = create_crypto_func('EVP_PKEY_derive_set_peer', EVP_PKEY_CTX_POINTER, EVP_PKEY_POINTER)
EVP_PKEY_derive = create_crypto_func('EVP_PKEY_derive', EVP_PKEY_CTX_POINTER, c_char_p, POINTER(c_size_t))
EVP_CIPHER_CTX_free = create_crypto_func('EVP_CIPHER_CTX_free', EVP_CIPHER_CTX_POINTER, restype=None)
EVP_get_cipherbyname = create_crypto_func('EVP_get_cipherbyname', c_char_p, restype=EVP_CIPHER_POINTER)
EVP_CIPHER_key_length = create_crypto_func('EVP_CIPHER_key_length', EVP_CIPHER_POINTER, int_return_ok=bool)
EVP_CIPHER_iv_length = create_crypto_func('EVP_CIPHER_iv_length', EVP_CIPHER_POINTER, int_return_ok=bool)
EVP_CIPHER_CTX_block_size = create_crypto_func('EVP_CIPHER_CTX_block_size', EVP_CIPHER_CTX_POINTER, int_return_ok=bool)
EVP_CIPHER_CTX_new = create_crypto_func('EVP_CIPHER_CTX_new', restype=EVP_CIPHER_CTX_POINTER)
EVP_EncryptInit_ex = create_crypto_func('EVP_EncryptInit_ex', EVP_CIPHER_CTX_POINTER, EVP_CIPHER_POINTER, c_void_p, c_char_p, c_char_p)
EVP_EncryptUpdate = create_crypto_func('EVP_EncryptUpdate', EVP_CIPHER_CTX_POINTER, c_char_p, POINTER(c_int), c_char_p, c_int)
EVP_EncryptFinal_ex = create_crypto_func('EVP_EncryptFinal_ex', EVP_CIPHER_CTX_POINTER, c_char_p, POINTER(c_int))
EVP_CIPHER_CTX_ctrl = create_crypto_func('EVP_CIPHER_CTX_ctrl', EVP_CIPHER_CTX_POINTER, c_int, c_int, c_char_p)
try:
EVP_CIPHER_CTX_tag_length = create_crypto_func('EVP_CIPHER_CTX_tag_length', EVP_CIPHER_CTX_POINTER, int_return_ok=bool)
except AttributeError: # need openssl >= 3
def EVP_CIPHER_CTX_tag_length(cipher):
return 16
EVP_CTRL_AEAD_GET_TAG, EVP_CTRL_AEAD_SET_TAG = 0x10, 0x11 # these are defines in the header dont know how to get them programmatically
EVP_CTRL_AEAD_SET_TAG
def abort_on_openssl_error():
stderr = c_void_p.in_dll(libc, 'stderr')
ERR_print_errors_fp(stderr)
raise SystemExit(1)
def elliptic_curve_keypair(algorithm='X25519'):
nid = OBJ_txt2nid(algorithm.encode())
pctx = EVP_PKEY_CTX_new_id(nid, None)
EVP_PKEY_keygen_init(pctx)
key = EVP_PKEY_POINTER()
EVP_PKEY_keygen(pctx, byref(key))
key.algorithm = nid
return key
def encrypt(plaintext, symmetric_key, algorithm='aes-256-gcm'):
cipher = EVP_get_cipherbyname(algorithm.encode())
if len(symmetric_key) != EVP_CIPHER_key_length(cipher):
raise KeyError(f'The symmetric key has length {len(symmetric_key)} != {EVP_CIPHER_key_length(cipher)} needed for {algorithm}')
ctx = EVP_CIPHER_CTX_new()
iv = os.urandom(EVP_CIPHER_iv_length(cipher))
EVP_EncryptInit_ex(ctx, cipher, None, symmetric_key, iv)
bs = EVP_CIPHER_CTX_block_size(ctx)
ciphertext = create_string_buffer(len(plaintext) + 2 * bs)
outlen = c_int(len(ciphertext))
EVP_EncryptUpdate(ctx, ciphertext, byref(outlen), plaintext, len(plaintext))
ans = ciphertext[:outlen.value]
outlen = c_int(len(ciphertext))
EVP_EncryptFinal_ex(ctx, ciphertext, byref(outlen))
if outlen.value:
ans += ciphertext[:outlen.value]
tag = create_string_buffer(EVP_CIPHER_CTX_tag_length(cipher))
EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, len(tag), tag)
return iv, ans, tag.raw
return elliptic_curve_keypair, encrypt
# }}}
# utils {{{
def encrypt_cmd(cmd, password, pubkey=None):
elliptic_curve_keypair, encrypt = build_crypto_tools()
if pubkey is None:
pubkey = os.environ['KITTY_PUBLIC_KEY']
v, d = pubkey.split(':', 1)
if v != '1':
raise SystemExit(f'Unsupported encryption protocol: {v}')
pubkey = base64.b85decode(d)
k = elliptic_curve_keypair()
sk = k.derive_secret(pubkey)
cmd['timestamp'] = time.time_ns()
cmd['password'] = password
data = json.dumps(cmd).encode()
iv, encrypted, tag = encrypt(data, sk)
def e(x):
return base64.b85encode(x).decode('ascii')
return {
'encrypted': e(encrypted), 'iv': e(iv), 'tag': e(tag), 'pubkey': e(k.public), 'version': cmd['version']
}
@contextmanager
def raw_mode(fd):
old = termios.tcgetattr(fd)
try:
tty.setraw(fd)
yield
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
def config_dir():
if 'KITTY_CONFIG_DIRECTORY' in os.environ:
return os.path.abspath(os.path.expanduser(os.environ['KITTY_CONFIG_DIRECTORY']))
locations = []
if 'XDG_CONFIG_HOME' in os.environ:
locations.append(os.path.abspath(os.path.expanduser(os.environ['XDG_CONFIG_HOME'])))
locations.append(os.path.expanduser('~/.config'))
if is_macos:
locations.append(os.path.expanduser('~/Library/Preferences'))
for loc in filter(None, os.environ.get('XDG_CONFIG_DIRS', '').split(os.pathsep)):
locations.append(os.path.abspath(os.path.expanduser(loc)))
for loc in locations:
if loc:
q = os.path.join(loc, 'kitty')
if os.access(q, os.W_OK) and os.path.exists(os.path.join(q, 'kitty.conf')):
return q
for loc in locations:
if loc:
q = os.path.join(loc, 'kitty')
if os.path.isdir(q) and os.access(q, os.W_OK):
return q
return ''
def resolve_custom_file(path):
path = os.path.expanduser(path)
path = os.path.expandvars(path)
if not os.path.isabs(path):
cdir = config_dir()
if cdir:
path = os.path.join(cdir, path)
return path
def get_password(opts):
if opts.use_password == 'never':
return ''
ans = ''
if opts.password:
ans = opts.password
if not ans and opts.password_file:
if opts.password_file == '-':
if sys.stdin.isatty():
from getpass import getpass
ans = getpass()
else:
ans = sys.stdin.read().rstrip()
try:
tty_fd = os.open(os.ctermid(), os.O_RDONLY | os.O_CLOEXEC)
except OSError:
pass
else:
with open(tty_fd, closefd=True):
os.dup2(tty_fd, sys.stdin.fileno())
else:
try:
with open(resolve_custom_file(opts.password_file)) as f:
ans = f.read().rstrip()
except OSError:
pass
if not ans and opts.password_env:
ans = os.environ.get(opts.password_env, '')
if not ans and opts.use_password == 'always':
raise SystemExit('No password was found')
if ans and len(ans) > 1024:
raise SystemExit('Specified password is too long')
return ans
# }}}
arg_parser = argparse.ArgumentParser(prog='kitty@', description='Control kitty remotely.')
arg_parser.add_argument('--password', default='', help='''\
A password to use when contacting kitty. This will cause kitty to ask the user
for permission to perform the specified action, unless the password has been
accepted before or is pre-configured in kitty.conf''')
arg_parser.add_argument('--password-file', default='rc-pass', help='''\
A file from which to read the password. Trailing whitespace is ignored. Relative
paths are resolved from the kitty configuration directory. Use - to read from STDIN.
Used if no --password is supplied. Defaults to checking for the
rc-pass file in the kitty configuration directory.''')
arg_parser.add_argument('--password-env', default='KITTY_RC_PASSWORD', help='''\
The name of an environment variable to read the password from.
Used if no --password-file is supplied. Defaults to checking the KITTY_RC_PASSWORD.''')
arg_parser.add_argument('--use-password', default='if-available', choices=('if-available', 'always', 'never'), help='''\
If no password is available, kitty will usually just send the remote control command
without a password. This option can be used to force it to always or never use
the supplied password.''')
args = arg_parser.parse_args()
def populate_cmd(cmd):
raise NotImplementedError()
password = get_password(args)
cmd = {'version': [0, 20, 0]} # use a random version that's fairly old
populate_cmd(cmd)
if password:
encrypt_cmd(cmd, password)
# cmd = {'version': [0, 14, 2], 'cmd': 'ls'}
# cmd = encrypt_cmd(cmd, 'test')
# with open(os.open(os.ctermid(), os.O_RDWR | os.O_CLOEXEC), 'w') as tty_file, raw_mode(tty_file.fileno()):
# print(end=f'\x1bP@kitty-cmd{json.dumps(cmd)}\x1b\\', flush=True, file=tty_file)
# os.read(tty_file.fileno(), 4096)

View File

@@ -5,22 +5,21 @@ The :command:`launch` command
|kitty| has a :code:`launch` action that can be used to run arbitrary programs |kitty| has a :code:`launch` action that can be used to run arbitrary programs
in new windows/tabs. It can be mapped to user defined shortcuts in kitty.conf. in new windows/tabs. It can be mapped to user defined shortcuts in
It is very powerful and allows sending the contents of :file:`kitty.conf`. It is very powerful and allows sending the contents of the
the current window to the launched program, as well as many other options. current window to the launched program, as well as many other options.
In the simplest form, you can use it to open a new kitty window running the In the simplest form, you can use it to open a new kitty window running the
shell, as shown below:: shell, as shown below::
map f1 launch map f1 launch
To run a different program simply pass the command line as arguments to To run a different program simply pass the command line as arguments to launch::
launch::
map f1 launch vim path/to/some/file map f1 launch vim path/to/some/file
To open a new window with the same working directory as the currently To open a new window with the same working directory as the currently active
active window:: window::
map f1 launch --cwd=current map f1 launch --cwd=current
@@ -30,9 +29,10 @@ To open the new window in a new tab::
To run multiple commands in a shell, use:: To run multiple commands in a shell, use::
map f1 launch sh -c "ls && zsh" map f1 launch sh -c "ls && exec zsh"
To pass the contents of the current screen and scrollback to the started process:: To pass the contents of the current screen and scrollback to the started
process::
map f1 launch --stdin-source=@screen_scrollback less map f1 launch --stdin-source=@screen_scrollback less
@@ -46,16 +46,16 @@ There are many more powerful options, refer to the complete list below.
map f1 launch_tab vim map f1 launch_tab vim
map f2 launch_tab emacs map f2 launch_tab emacs
The :kbd:`F1` key will now open vim in a new tab with the current windows The :kbd:`F1` key will now open :program:`vim` in a new tab with the current
working directory windows working directory.
The piping environment The piping environment
-------------------------- --------------------------
When using :option:`launch --stdin-source`, the program to which the data is When using :option:`launch --stdin-source`, the program to which the data is
piped has a special environment variable declared, :envvar:`KITTY_PIPE_DATA` whose piped has a special environment variable declared, :envvar:`KITTY_PIPE_DATA`
contents are:: whose contents are::
KITTY_PIPE_DATA={scrolled_by}:{cursor_x},{cursor_y}:{lines},{columns} KITTY_PIPE_DATA={scrolled_by}:{cursor_x},{cursor_y}:{lines},{columns}
@@ -73,33 +73,33 @@ the command line:
``@selection`` ``@selection``
replaced by the currently selected text Replaced by the currently selected text.
``@active-kitty-window-id`` ``@active-kitty-window-id``
replaced by the id of the currently active kitty window Replaced by the id of the currently active kitty window.
``@line-count`` ``@line-count``
replaced by the number of lines in STDIN. Only present when passing some Replaced by the number of lines in STDIN. Only present when passing some
data to STDIN data to STDIN.
``@input-line-number`` ``@input-line-number``
replaced the number of lines a pager should scroll to match the current Replaced the number of lines a pager should scroll to match the current
scroll position in kitty. See :opt:`scrollback_pager` for details scroll position in kitty. See :opt:`scrollback_pager` for details.
``@scrolled-by`` ``@scrolled-by``
replaced by the number of lines kitty is currently scrolled by Replaced by the number of lines kitty is currently scrolled by.
``@cursor-x`` ``@cursor-x``
replaced by the current cursor x position with 1 being the leftmost cell Replaced by the current cursor x position with 1 being the leftmost cell.
``@cursor-y`` ``@cursor-y``
replaced by the current cursor y position with 1 being the topmost cell Replaced by the current cursor y position with 1 being the topmost cell.
``@first-line-on-screen`` ``@first-line-on-screen``
replaced by the first line on screen. Can be used for pager positioning. Replaced by the first line on screen. Can be used for pager positioning.
``@last-line-on-screen`` ``@last-line-on-screen``
replaced by the last line on screen. Can be used for pager positioning. Replaced by the last line on screen. Can be used for pager positioning.
For example:: For example::
@@ -112,44 +112,51 @@ For example::
Watching launched windows Watching launched windows
--------------------------- ---------------------------
The :option:`launch --watcher` option allows you to specify python functions 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 that will be called at specific events, such as when the window is resized or
closed. Simply specify the path to a python module that specifies callback closed. Simply specify the path to a Python module that specifies callback
functions for the events you are interested in, for example: functions for the events you are interested in, for example:
.. code-block:: python .. code-block:: python
def on_resize(boss, window, data): from typing import Any, Dict
from kitty.boss import Boss
from kitty.window import Window
def on_resize(boss: Boss, window: Window, data: Dict[str, Any]) -> None:
# Here data will contain old_geometry and new_geometry # Here data will contain old_geometry and new_geometry
def on_focus_change(boss, window, data): def on_focus_change(boss: Boss, window: Window, data: Dict[str, Any])-> None:
# Here data will contain focused # Here data will contain focused
def on_close(boss, window, data): def on_close(boss: Boss, window: Window, data: Dict[str, Any])-> None:
# called when window is closed, typically when the program running in # called when window is closed, typically when the program running in
# it exits. # it exits.
Every callback is passed a reference to the global ``Boss`` object as well as 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 the ``Window`` object the action is occurring on. The ``data`` object is a dict
a dict that contains event dependent data. Some useful methods and attributes that contains event dependent data. Some useful methods and attributes for the
for the ``Window`` object are: ``as_text(as_ans=False, add_history=False, ``Window`` object are: ``as_text(as_ans=False, add_history=False,
add_wrap_markers=False, alternate_screen=False)`` with which you can get the add_wrap_markers=False, alternate_screen=False)`` with which you can get the
contents of the window and its scrollback buffer. Similarly, contents of the window and its scrollback buffer. Similarly,
``window.child.pid`` is the PID of the processes that was launched ``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 in the window and ``window.id`` is the internal kitty ``id`` of the window.
window.
Finding executables Finding executables
----------------------- -----------------------
When you specify a command to run as just a name rather than an absolute path, When you specify a command to run as just a name rather than an absolute path,
it is searched for in the system-wide ``PATH`` environment variable. Note that it is searched for in the system-wide :envvar:`PATH` environment variable. Note
this **may not** be the value of ``PATH`` inside a shell, as shell startup scripts that this **may not** be the value of :envvar:`PATH` inside a shell, as shell
often change the value of this variable. If it is not found there, then a startup scripts often change the value of this variable. If it is not found
system specific list of default paths is searched. If it is still not found, there, then a system specific list of default paths is searched. If it is still
then your shell is run and the value of ``PATH`` inside the shell is used. not found, then your shell is run and the value of :envvar:`PATH` inside the
shell is used.
See :opt:`exe_search_path` for details and how to control this. See :opt:`exe_search_path` for details and how to control this.
Syntax reference Syntax reference

View File

@@ -10,7 +10,8 @@ other in arbitrary arrangements, based on *Layouts*, see below for examples:
:align: center :align: center
:width: 100% :width: 100%
Screenshot, showing vim, tig and git running in |kitty| with the 'Tall' layout Screenshot, showing :program:`vim`, :program:`tig` and :program:`git`
running in |kitty| with the *Tall* layout
.. figure:: screenshots/splits.png .. figure:: screenshots/splits.png
@@ -18,21 +19,22 @@ other in arbitrary arrangements, based on *Layouts*, see below for examples:
:align: center :align: center
:width: 100% :width: 100%
Screenshot, showing windows with arbitrary arrangement in the 'Splits' Screenshot, showing windows with arbitrary arrangement in the *Splits*
layout layout
There are many different layouts available. They are all enabled by default, There are many different layouts available. They are all enabled by default, you
you can switch layouts using :sc:`next_layout`. To control which layouts can switch layouts using :ac:`next_layout` (:sc:`next_layout` by default). To
are available use :opt:`enabled_layouts`, the first listed layout becomes control which layouts are available use :opt:`enabled_layouts`, the first listed
the default. Individual layouts and how to use them are described below. layout becomes the default. Individual layouts and how to use them are described
below.
The Stack Layout The Stack Layout
------------------ ------------------
This is the simplest layout it displays a single window using all available This is the simplest layout. It displays a single window using all available
space, other windows are hidden behind it. It has no options:: space, other windows are hidden behind it. This layout has no options::
enabled_layouts stack enabled_layouts stack
@@ -40,14 +42,14 @@ space, other windows are hidden behind it. It has no options::
The Tall Layout The Tall Layout
------------------ ------------------
Displays one (or optionally more) full height windows on the left half of the Displays one (or optionally more) full-height windows on the left half of the
screen. Remaining windows are tiled vertically on the right half of the screen. screen. Remaining windows are tiled vertically on the right half of the screen.
There are options to control how the screen is split horizontally ``bias`` There are options to control how the screen is split horizontally ``bias``
(an integer between ``10`` and ``90``) and options to control how many (an integer between ``10`` and ``90``) and options to control how many
full-height windows there are ``full_size`` (a positive integer). The full-height windows there are ``full_size`` (a positive integer). The
``mirrored`` option when set to ``true`` will cause the short windows to be ``mirrored`` option when set to ``true`` will cause the full-height windows to
on the left side of the screen instead of the right. The syntax be on the right side of the screen instead of the left. The syntax
for the options is shown below:: for the options is::
enabled_layouts tall:bias=50;full_size=1;mirrored=false enabled_layouts tall:bias=50;full_size=1;mirrored=false
@@ -65,24 +67,32 @@ for the options is shown below::
│ │ │ │ │ │
└──────────────┴───────────────┘ └──────────────┴───────────────┘
In addition, you can map keys to increase or decrease the number of full size In addition, you can map keys to increase or decrease the number of full-height
windows, for example:: windows, or toggle the mirrored setting, for example::
map ctrl+[ layout_action decrease_num_full_size_windows map ctrl+[ layout_action decrease_num_full_size_windows
map ctrl+] layout_action increase_num_full_size_windows map ctrl+] layout_action increase_num_full_size_windows
map ctrl+/ layout_action mirror toggle
map ctrl+y layout_action mirror true
map ctrl+n layout_action mirror false
You can also map a key to change the bias by providing a list of percentages
and it will rotate through the list as you press the key. If you only provide
one number it'll toggle between that percentage and 50, for example::
map ctrl+. layout_action bias 50 62 70
map ctrl+, layout_action bias 62
The Fat Layout The Fat Layout
---------------- ----------------
Displays one (or optionally more) full width windows on the top half of the Displays one (or optionally more) full-width windows on the top half of the
screen. Remaining windows are tiled horizontally on the bottom half of the screen. screen. Remaining windows are tiled horizontally on the bottom half of the
There are options to control how the screen is split vertically ``bias`` screen. There are options to control how the screen is split vertically ``bias``
(an integer between ``10`` and ``90``) and options to control how many (an integer between ``10`` and ``90``) and options to control how many
full-height windows there are ``full_size`` (a positive integer). The full-width windows there are ``full_size`` (a positive integer). The
``mirrored`` option when set to ``true`` will cause the narrow windows to be ``mirrored`` option when set to ``true`` will cause the full-width windows to be
on the top of the screen instead of the bottom. The syntax for the options is on the bottom of the screen instead of the top. The syntax for the options is::
shown below::
enabled_layouts fat:bias=50;full_size=1;mirrored=false enabled_layouts fat:bias=50;full_size=1;mirrored=false
@@ -100,11 +110,15 @@ shown below::
└─────────┴──────────┴─────────┘ └─────────┴──────────┴─────────┘
This layout also supports the same layout actions as the *Tall* layout, shown above.
The Grid Layout The Grid Layout
-------------------- --------------------
Display windows in a balanced grid with all windows the same size except the Display windows in a balanced grid with all windows the same size except the
last column if there are not enough windows to fill the grid. Has no options:: last column if there are not enough windows to fill the grid. This layout has no
options::
enabled_layouts grid enabled_layouts grid
@@ -132,20 +146,20 @@ define a few extra key bindings in :file:`kitty.conf`::
# Create a new window splitting the space used by the existing one so that # Create a new window splitting the space used by the existing one so that
# the two windows are placed one above the other # the two windows are placed one above the other
map F5 launch --location=hsplit map f5 launch --location=hsplit
# Create a new window splitting the space used by the existing one so that # Create a new window splitting the space used by the existing one so that
# the two windows are placed side by side # the two windows are placed side by side
map F6 launch --location=vsplit map f6 launch --location=vsplit
# Create a new window splitting the space used by the existing one so that # Create a new window splitting the space used by the existing one so that
# the two windows are placed side by side if the existing window is wide or # the two windows are placed side by side if the existing window is wide or
# one above the other if the existing window is tall. # one above the other if the existing window is tall.
map F4 launch --location=split map f4 launch --location=split
# Rotate the current split, chaging its split axis from vertical to # Rotate the current split, chaging its split axis from vertical to
# horizontal or vice versa # horizontal or vice versa
map F7 layout_action rotate map f7 layout_action rotate
# Move the active window in the indicated direction # Move the active window in the indicated direction
map shift+up move_window up map shift+up move_window up
@@ -159,16 +173,16 @@ define a few extra key bindings in :file:`kitty.conf`::
map ctrl+up neighboring_window up map ctrl+up neighboring_window up
map ctrl+down neighboring_window down map ctrl+down neighboring_window down
Windows can be resized using :ref:`window_resizing`. You can swap the windows Windows can be resized using :ref:`window_resizing`. You can swap the windows
in a split using the ``rotate`` action with an argument of ``180`` and rotate in a split using the ``rotate`` action with an argument of ``180`` and rotate
and swap with an argument of ``270``. and swap with an argument of ``270``.
This layout takes one option, ``split_axis`` that controls whether new windows This layout takes one option, ``split_axis`` that controls whether new windows
are placed into vertical or horizontal splits when a ``--location`` is not are placed into vertical or horizontal splits when a :option:`--location <launch
specified. A value of ``horizontal`` (same as ``--location=vsplit``) --location>` is not specified. A value of ``horizontal`` (same as
means when a new split is created the two windows will be placed side by side ``--location=vsplit``) means when a new split is created the two windows will be
and a value of ``vertical`` (same as ``--location=hsplit``) means the two placed side by side and a value of ``vertical`` (same as ``--location=hsplit``)
windows will be placed one on top of the other. By default:: means the two windows will be placed one on top of the other. By default::
enabled_layouts splits:split_axis=horizontal enabled_layouts splits:split_axis=horizontal
@@ -193,7 +207,7 @@ windows will be placed one on top of the other. By default::
The Horizontal Layout The Horizontal Layout
------------------------ ------------------------
All windows are shown side by side. Has no options:: All windows are shown side by side. This layout has no options::
enabled_layouts horizontal enabled_layouts horizontal
@@ -213,7 +227,7 @@ All windows are shown side by side. Has no options::
The Vertical Layout The Vertical Layout
----------------------- -----------------------
All windows are shown one below the other. Has no options:: All windows are shown one below the other. This layout has no options::
enabled_layouts vertical enabled_layouts vertical
@@ -239,14 +253,14 @@ Resizing windows
You can resize windows inside layouts. Press :sc:`start_resizing_window` (also You can resize windows inside layouts. Press :sc:`start_resizing_window` (also
:kbd:`⌘+r` on macOS) to enter resizing mode and follow the on-screen :kbd:`⌘+r` on macOS) to enter resizing mode and follow the on-screen
instructions. In a given window layout only some operations may be possible instructions. In a given window layout only some operations may be possible for
for a particular window. For example, in the Tall layout you can make the first a particular window. For example, in the *Tall* layout you can make the first
window wider/narrower, but not taller/shorter. Note that what you are resizing window wider/narrower, but not taller/shorter. Note that what you are resizing
is actually not a window, but a row/column in the layout, all windows in that is actually not a window, but a row/column in the layout, all windows in that
row/column will be resized. row/column will be resized.
You can also define shortcuts in :file:`kitty.conf` to make the active window You can also define shortcuts in :file:`kitty.conf` to make the active window
wider, narrower, taller, or shorter by mapping to the ``resize_window`` wider, narrower, taller, or shorter by mapping to the :ac:`resize_window`
action, for example:: action, for example::
map ctrl+left resize_window narrower map ctrl+left resize_window narrower
@@ -256,22 +270,21 @@ action, for example::
# reset all windows in the tab to default sizes # reset all windows in the tab to default sizes
map ctrl+home resize_window reset map ctrl+home resize_window reset
The ``resize_window`` action has a second, optional argument to control The :ac:`resize_window` action has a second optional argument to control
the resizing increment (a positive integer that defaults to 1). the resizing increment (a positive integer that defaults to 1).
Some layouts take options to control their behavior. For example, the *Fat*
Some layouts take options to control their behavior. For example, the ``fat`` and *Tall* layouts accept the ``bias`` and ``full_size`` options to control
and ``tall`` layouts accept the ``bias`` and ``full_size`` options to control how the available space is split up. To specify the option, in :opt:`kitty.conf
how the available space is split up. <enabled_layouts>` use::
To specify the option, in :opt:`kitty.conf <enabled_layouts>` use::
enabled_layouts tall:bias=70;full_size=2 enabled_layouts tall:bias=70;full_size=2
This will have ``2`` instead of a single tall window, that occupy ``70%`` This will have ``2`` instead of a single tall window, that occupy ``70%``
instead of ``50%`` of available width. ``bias`` can be any number between 10 instead of ``50%`` of available width. ``bias`` can be any number between ``10``
and 90. and ``90``.
Writing a new layout only requires about two hundred lines of code, so if there Writing a new layout only requires about two hundred lines of code, so if there
is some layout you want, take a look at one of the existing layouts in the is some layout you want, take a look at one of the existing layouts in the
`layout <https://github.com/kovidgoyal/kitty/tree/master/kitty/layout>`_ `layout <https://github.com/kovidgoyal/kitty/tree/master/kitty/layout>`__
package and submit a pull request! package and submit a pull request!

View File

@@ -9,12 +9,12 @@ running programs or similar. Lets start with a few examples:
Examples Examples
---------- ----------
Suppose we want to be able to highlight the word ERROR in the current window. Suppose we want to be able to highlight the word :code:`ERROR` in the current
Add the following to :file:`kitty.conf`:: window. Add the following to :file:`kitty.conf`::
map f1 toggle_marker text 1 ERROR map f1 toggle_marker text 1 ERROR
Now when you press :kbd:`F1` all instances of the word :code:`ERROR` will be Now when you press :kbd:`F1`, all instances of the word :code:`ERROR` will be
highlighted. To turn off the highlighting, press :kbd:`F1` again. highlighted. To turn off the highlighting, press :kbd:`F1` again.
If you want to make it case-insensitive, use:: If you want to make it case-insensitive, use::
@@ -39,41 +39,40 @@ can control the colors used for these groups in :file:`kitty.conf` with::
.. note:: .. note::
For performance reasons, matching is done per line only, and only when that line is For performance reasons, matching is done per line only, and only when that
altered in any way. So you cannot match text that stretches across multiple line is altered in any way. So you cannot match text that stretches across
lines. multiple lines.
Creating markers dynamically Creating markers dynamically
--------------------------------- ---------------------------------
If you want to create markers dynamically rather than pre-defining them in If you want to create markers dynamically rather than pre-defining them in
:file:`kitty.conf` you can do so as follows:: :file:`kitty.conf`, you can do so as follows::
map f1 create_marker map f1 create_marker
map f2 remove_marker map f2 remove_marker
Then pressing :kbd:`F1` will allow you to enter the marker definition and set Then pressing :kbd:`F1` will allow you to enter the marker definition and set it
it and pressing :kbd:`F2` will remove the marker. ``create_marker`` accepts and pressing :kbd:`F2` will remove the marker. :ac:`create_marker` accepts the
the same syntax as ``toggle_marker`` above. Note that while creating same syntax as :ac:`toggle_marker` above. Note that while creating markers, the
markers, the prompt has history so you can easily re-use previous marker prompt has history so you can easily re-use previous marker expressions.
expressions.
You can also use the facilities for :doc:`remote-control` to dynamically You can also use the facilities for :doc:`remote-control` to dynamically add or
add/remove markers. remove markers.
Scrolling to marks Scrolling to marks
-------------------- --------------------
kitty has an action to scroll to the next line that contains a mark. You can kitty has a :ac:`scroll_to_mark` action to scroll to the next line that contains
use it by mapping it to some shortcut in :file:`kitty.conf`:: a mark. You can use it by mapping it to some shortcut in :file:`kitty.conf`::
map ctrl+p scroll_to_mark prev map ctrl+p scroll_to_mark prev
map ctrl+n scroll_to_mark next map ctrl+n scroll_to_mark next
Then pressing :kbd:`ctrl+p` will scroll to the first line in the scrollback Then pressing :kbd:`Ctrl+P` will scroll to the first line in the scrollback
buffer above the current top line that contains a mark. Pressing :kbd:`ctrl+n` buffer above the current top line that contains a mark. Pressing :kbd:`Ctrl+N`
will scroll to show the first line below the current last line that contains will scroll to show the first line below the current last line that contains
a mark. If you wish to jump to a mark of a specific type, you can add that to a mark. If you wish to jump to a mark of a specific type, you can add that to
the mapping:: the mapping::
@@ -86,26 +85,26 @@ Which will scroll only to marks of type 1.
The full syntax for creating marks The full syntax for creating marks
------------------------------------- -------------------------------------
The syntax of the :code:`toggle_marker` command is:: The syntax of the :ac:`toggle_marker` action is::
toggle_marker <marker-type> <specification> toggle_marker <marker-type> <specification>
Here :code:`marker-type` is one of: Here :code:`marker-type` is one of:
* :code:`text` - simple substring matching * :code:`text` - Simple substring matching
* :code:`itext` - case-insensitive substring matching * :code:`itext` - Case-insensitive substring matching
* :code:`regex` - A python regular expression * :code:`regex` - A Python regular expression
* :code:`iregex` - A case-insensitive python regular expression * :code:`iregex` - A case-insensitive Python regular expression
* :code:`function` - An arbitrary function defined in a python file, see :ref:`marker_funcs`. * :code:`function` - An arbitrary function defined in a Python file, see :ref:`marker_funcs`.
.. _marker_funcs: .. _marker_funcs:
Arbitrary marker functions Arbitrary marker functions
----------------------------- -----------------------------
You can create your own marker functions. Create a python file named You can create your own marker functions. Create a Python file named
:file:`mymarker.py` and in it create a :code:`marker` function. This :file:`mymarker.py` and in it create a :code:`marker` function. This function
function receives the text of the line as input and must yield three numbers, receives the text of the line as input and must yield three numbers,
the starting character position, the ending character position and the mark the starting character position, the ending character position and the mark
group (1-3). For example: group (1-3). For example:
@@ -122,6 +121,7 @@ Save this file somewhere and in :file:`kitty.conf`, use::
map f1 toggle_marker function /path/to/mymarker.py map f1 toggle_marker function /path/to/mymarker.py
If you save the file in the kitty config directory, you can use:: If you save the file in the :ref:`kitty config directory <confloc>`, you can
use::
map f1 toggle_marker function mymarker.py map f1 toggle_marker function mymarker.py

View File

@@ -1,15 +1,14 @@
Scripting the mouse click Scripting the mouse click
====================================================== ======================================================
|kitty| has support for `terminal hyperlinks |kitty| has support for :term:`terminal hyperlinks <hyperlinks>`. These are
<https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>`_. These generated by many terminal programs, such as ``ls``, ``gcc``, ``systemd``,
are generated by many terminal programs, such as ``ls``, ``gcc``, ``systemd``, :ref:`tool_mdcat`, etc. You can customize exactly what happens when clicking on
:ref:`tool_mdcat`, etc. You can customize exactly what happens when clicking on these these hyperlinks in |kitty|.
hyperlinks in |kitty|.
You can tell kitty to take arbitrarily many, complex actions You can tell kitty to take arbitrarily many, complex actions when a link is
when a link is clicked. Let us illustrate with some examples, first. Create clicked. Let us illustrate with some examples, first. Create the file
the file :file:`~/.config/kitty/open-actions.conf` with the following: :file:`~/.config/kitty/open-actions.conf` with the following:
.. code:: conf .. code:: conf
@@ -22,6 +21,13 @@ Now, run ``ls --hyperlink=auto`` in kitty and click on the filename of an
image, holding down :kbd:`ctrl+shift`. It will be opened over the current image, holding down :kbd:`ctrl+shift`. It will be opened over the current
window. Press any key to close it. window. Press any key to close it.
.. note::
The :program:`ls` comes with macOS does not support hyperlink, you need to
install `GNU Coreutils <https://www.gnu.org/software/coreutils/>`__. If you
install it via `Homebrew <https://formulae.brew.sh/formula/coreutils>`__, it
will be :program:`gls`.
Each entry in :file:`open-actions.conf` consists of one or more Each entry in :file:`open-actions.conf` consists of one or more
:ref:`matching_criteria`, such as ``protocol`` and ``mime`` and one or more :ref:`matching_criteria`, such as ``protocol`` and ``mime`` and one or more
``action`` entries. In the example above kitty uses the :doc:`launch <launch>` ``action`` entries. In the example above kitty uses the :doc:`launch <launch>`
@@ -29,8 +35,8 @@ action which can be used to run external programs. Entries are separated by
blank lines. blank lines.
Actions are very powerful, anything that you can map to a key combination in Actions are very powerful, anything that you can map to a key combination in
`kitty.conf` can be used as an action. You can specify more than one action per :file:`kitty.conf` can be used as an action. You can specify more than one
entry if you like, for example: action per entry if you like, for example:
.. code:: conf .. code:: conf
@@ -58,9 +64,13 @@ some special variables, documented below:
``FRAGMENT`` ``FRAGMENT``
The fragment (unquoted), if any of the URL or the empty string. The fragment (unquoted), if any of the URL or the empty string.
``URL_PATH``
The path, query and fragment portions of the URL, without any
unquoting.
.. note:: .. note::
You can use the :opt:`action_alias` option just as in kitty.conf to You can use the :opt:`action_alias` option just as in :file:`kitty.conf` to
define aliases for frequently used actions. define aliases for frequently used actions.
@@ -77,7 +87,7 @@ lines. The various available criteria are:
``protocol`` ``protocol``
A comma separated list of protocols, for example: ``http, https``. If A comma separated list of protocols, for example: ``http, https``. If
absent, there is no constraint on protocol absent, there is no constraint on protocol.
``url`` ``url``
A regular expression that must match against the entire (unquoted) URL A regular expression that must match against the entire (unquoted) URL
@@ -88,11 +98,12 @@ lines. The various available criteria are:
``mime`` ``mime``
A comma separated list of MIME types, for example: ``text/*, image/*, A comma separated list of MIME types, for example: ``text/*, image/*,
application/pdf``. You can add MIME types to kitty by creating the application/pdf``. You can add MIME types to kitty by creating a file named
:file:`mime.types` in the kitty configuration directory. Useful if your :file:`mime.types` in the :ref:`kitty configuration directory <confloc>`.
system MIME database does not have definitions you need. This file is Useful if your system MIME database does not have definitions you need. This
in the standard format of one definition per line, like: ``text/plain rst file is in the standard format of one definition per line, like:
md``. Note that the MIME type for directories is ``inode/directory``. ``text/plain rst md``. Note that the MIME type for directories is
``inode/directory``.
``ext`` ``ext``
A comma separated list of file extensions, for example: ``jpeg, tar.gz`` A comma separated list of file extensions, for example: ``jpeg, tar.gz``
@@ -116,9 +127,10 @@ URLs onto the kitty dock icon to open them with kitty. The default actions are:
These actions can also be executed from the command line by running:: These actions can also be executed from the command line by running::
open -a kitty.app file_or_url ... (on macOS only) kitty +open file_or_url another_url ...
or
kitty +open file_or_url ... # macOS only
open -a kitty.app file_or_url another_url ...
Since macOS lacks an official interface to set default URL scheme handlers, Since macOS lacks an official interface to set default URL scheme handlers,
kitty has a command you can use for it. The first argument for is the URL kitty has a command you can use for it. The first argument for is the URL
@@ -133,36 +145,10 @@ defaults to kitty, if not specified. For example:
kitty +runpy 'from kitty.fast_data_types import cocoa_set_url_handler; import sys; cocoa_set_url_handler(*sys.argv[1:]); print("OK")' xyz someapp.bundle.identifier kitty +runpy 'from kitty.fast_data_types import cocoa_set_url_handler; import sys; cocoa_set_url_handler(*sys.argv[1:]); print("OK")' xyz someapp.bundle.identifier
You can customize these actions by creating a :file:`launch-actions.conf` file You can customize these actions by creating a :file:`launch-actions.conf` file
in the kitty config directory, just like in the :ref:`kitty config directory <confloc>`, just like the
the :file:`open-actions.conf` file above. For example: :file:`open-actions.conf` file above. For example:
.. code:: conf .. literalinclude:: ../kitty/open_actions.py
:language: conf
# Open script files based on their shebangs :start-at: # Open script files
protocol file :end-before: '''.splitlines()))
ext sh,command,tool
action launch --hold --type=os-window kitty +shebang $FILE_PATH {SHELL}
# Open shell specific script files
protocol file
ext fish,bash,zsh
action launch --hold --type=os-window kitty +shebang $FILE_PATH __ext__
# Open directories
protocol file
mime inode/directory
action launch --type=os-window --cwd $FILE_PATH
# Open text files without fragments in the editor
protocol file
mime text/*
action launch --type=os-window $EDITOR $FILE_PATH
# Open image files with icat
protocol file
mime image/*
action launch --type=os-window kitty +kitten icat --hold $FILE_PATH
# Open ssh URLs with ssh command
protocol ssh
action launch --type=os-window ssh $URL

View File

@@ -4,23 +4,22 @@ Overview
Design philosophy Design philosophy
------------------- -------------------
|kitty| is designed for power keyboard users. To that end all its controls |kitty| is designed for power keyboard users. To that end all its controls work
work with the keyboard (although it fully supports mouse interactions as with the keyboard (although it fully supports mouse interactions as well). Its
well). Its configuration is a simple, human editable, single file for configuration is a simple, human editable, single file for easy reproducibility
easy reproducibility (I like to store configuration in source control). (I like to store configuration in source control).
The code in |kitty| is designed to be simple, modular and hackable. It is The code in |kitty| is designed to be simple, modular and hackable. It is
written in a mix of C (for performance sensitive parts) and Python (for written in a mix of C (for performance sensitive parts) and Python (for easy
easy hackability of the UI). It does not depend on any large and complex hackability of the UI). It does not depend on any large and complex UI toolkit,
UI toolkit, using only OpenGL for rendering everything. using only OpenGL for rendering everything.
Finally, |kitty| is designed from the ground up to support all modern Finally, |kitty| is designed from the ground up to support all modern terminal
terminal features, such as unicode, true color, bold/italic fonts, text features, such as Unicode, true color, bold/italic fonts, text formatting, etc.
formatting, etc. It even extends existing text formatting escape codes, It even extends existing text formatting escape codes, to add support for
to add support for features not available elsewhere, such as colored and features not available elsewhere, such as colored and styled (curly) underlines.
styled (curly) underlines. One of the design goals of |kitty| is to be One of the design goals of |kitty| is to be easily extensible so that new
easily extensible so that new features can be added in the future with features can be added in the future with relatively little effort.
relatively little effort.
.. include:: basic.rst .. include:: basic.rst
@@ -28,10 +27,10 @@ relatively little effort.
Configuring kitty Configuring kitty
------------------- -------------------
|kitty| is highly configurable, everything from keyboard shortcuts to |kitty| is highly configurable, everything from keyboard shortcuts to painting
painting frames-per-second. Press :sc:`edit_config_file` in kitty frames-per-second. Press :sc:`edit_config_file` in kitty to open its fully
to open its fully commented sample config file in your text editor. commented sample config file in your text editor. For details see the
For details see the :doc:`configuration docs <conf>`. :doc:`configuration docs <conf>`.
.. toctree:: .. toctree::
:hidden: :hidden:
@@ -51,17 +50,26 @@ windows automatically, resizing and moving them as needed. You can create a new
Currently, there are seven layouts available: Currently, there are seven layouts available:
* **Fat** -- One (or optionally more) windows are shown full width on the top, the rest of the windows are shown side-by-side on the bottom * **Fat** -- One (or optionally more) windows are shown full width on the top,
the rest of the windows are shown side-by-side on the bottom
* **Grid** -- All windows are shown in a grid * **Grid** -- All windows are shown in a grid
* **Horizontal** -- All windows are shown side-by-side * **Horizontal** -- All windows are shown side-by-side
* **Splits** -- Windows arranged in arbitrary patterns created using horizontal and vertical splits
* **Splits** -- Windows arranged in arbitrary patterns created using horizontal
and vertical splits
* **Stack** -- Only a single maximized window is shown at a time * **Stack** -- Only a single maximized window is shown at a time
* **Tall** -- One (or optionally more) windows are shown full height on the left, the rest of the windows are shown one below the other on the right
* **Tall** -- One (or optionally more) windows are shown full height on the
left, the rest of the windows are shown one below the other on the right
* **Vertical** -- All windows are shown one below the other * **Vertical** -- All windows are shown one below the other
By default, all layouts are enabled and you can switch between layouts using By default, all layouts are enabled and you can switch between layouts using
the :sc:`next_layout` key combination. You can also create shortcuts to select the :sc:`next_layout` key combination. You can also create shortcuts to select
particular layouts, and choose which layouts you want to enable/disable, see particular layouts, and choose which layouts you want to enable, see
:ref:`conf-kitty-shortcuts.layout` for examples. The first layout listed in :ref:`conf-kitty-shortcuts.layout` for examples. The first layout listed in
:opt:`enabled_layouts` becomes the default layout. :opt:`enabled_layouts` becomes the default layout.
@@ -79,9 +87,9 @@ Extending kitty
kitty has a powerful framework for scripting. You can create small terminal kitty has a powerful framework for scripting. You can create small terminal
programs called :doc:`kittens <kittens_intro>`. These can used to add features programs called :doc:`kittens <kittens_intro>`. These can used to add features
to kitty, for example, :doc:`editing remote files <kittens/remote_file>` or to kitty, for example, :doc:`editing remote files <kittens/remote_file>` or
:doc:`inputting unicode characters <kittens/unicode_input>`. They can also be :doc:`inputting Unicode characters <kittens/unicode_input>`. They can also be
used to create programs that leverage kitty's powerful features, for example, used to create programs that leverage kitty's powerful features, for example,
:doc:`viewing images <kittens/icat>` or :doc:`diffing files with images :doc:`viewing images <kittens/icat>` or :doc:`diffing files with image support
<kittens/diff>`. <kittens/diff>`.
You can :doc:`create your own kittens to scratch your own itches You can :doc:`create your own kittens to scratch your own itches
@@ -101,9 +109,9 @@ Remote control
|kitty| has a very powerful system that allows you to control it from the |kitty| has a very powerful system that allows you to control it from the
:doc:`shell prompt, even over SSH <remote-control>`. You can change colors, :doc:`shell prompt, even over SSH <remote-control>`. You can change colors,
fonts, open new :term:`windows <window>`, :term:`tabs <tab>`, set their titles, fonts, open new :term:`windows <window>`, :term:`tabs <tab>`, set their titles,
change window layout, get text change window layout, get text from one window and send text to another, etc.
from one window and send text to another, etc, etc. The possibilities are The possibilities are endless. See the :doc:`tutorial <remote-control>` to get
endless. See the :doc:`tutorial <remote-control>` to get started. started.
.. toctree:: .. toctree::
:hidden: :hidden:
@@ -117,10 +125,9 @@ Startup Sessions
------------------ ------------------
You can control the :term:`tabs <tab>`, :term:`kitty window <window>` layout, You can control the :term:`tabs <tab>`, :term:`kitty window <window>` layout,
working directory, startup programs, working directory, startup programs, etc. by creating a *session* file and using
etc. by creating a "session" file and using the :option:`kitty --session` the :option:`kitty --session` command line flag or the :opt:`startup_session`
command line flag or the :opt:`startup_session` option in :file:`kitty.conf`. option in :file:`kitty.conf`. For example:
For example:
.. code-block:: session .. code-block:: session
@@ -130,37 +137,39 @@ For example:
cd ~ cd ~
# Create a window and run the specified command in it # Create a window and run the specified command in it
launch zsh launch zsh
# Create a window with some environment variables set and run # Create a window with some environment variables set and run vim in it
# vim in it
launch --env FOO=BAR vim launch --env FOO=BAR vim
# Set the title for the next window # Set the title for the next window
launch --title "Chat with x" irssi --profile x launch --title "Chat with x" irssi --profile x
# Create a new tab (the part after new_tab is the optional tab # Create a new tab
# name which will be displayed in the tab bar, if omitted, the # The part after new_tab is the optional tab title which will be displayed in
# title of the active window will be used instead) # the tab bar, if omitted, the title of the active window will be used instead.
new_tab my tab new_tab my tab
cd ~/somewhere cd ~/somewhere
# Set the layouts allowed in this tab # Set the layouts allowed in this tab
enabled_layouts tall, stack enabled_layouts tall,stack
# Set the current layout # Set the current layout
layout stack layout stack
launch zsh launch zsh
# Create a new OS window # Create a new OS window
# Any definitions specifed before the first new_os_window will apply to first OS window.
new_os_window new_os_window
# set new window size to 80x25 cells # Set new window size to 80x24 cells
os_window_size 80c 25c os_window_size 80c 24c
# set the --class for the new OS window # Set the --class for the new OS window
os_window_class mywindow os_window_class mywindow
launch sh launch sh
# Resize the current window (see the resize_window action for details)
resize_window wider 2
# Make the current window the active (focused) window # Make the current window the active (focused) window
focus focus
launch emacs launch emacs
.. note:: .. note::
The :doc:`launch <launch>` command when used in a session file The :doc:`launch <launch>` command when used in a session file cannot create
cannot create new OS windows, or tabs. new OS windows, or tabs.
Creating tabs/windows Creating tabs/windows
@@ -182,25 +191,25 @@ Mouse features
* You can click on a URL to open it in a browser. * You can click on a URL to open it in a browser.
* You can double click to select a word and then drag to select more words. * You can double click to select a word and then drag to select more words.
* You can triple click to select a line and then drag to select more lines. * You can triple click to select a line and then drag to select more lines.
* You can triple click while holding :kbd:`ctrl+alt` to select from clicked * You can triple click while holding :kbd:`Ctrl+Alt` to select from clicked
point to end of line. point to end of line.
* You can right click to extend a previous selection. * You can right click to extend a previous selection.
* You can hold down :kbd:`ctrl+alt` and drag with the mouse to select in * You can hold down :kbd:`Ctrl+Alt` and drag with the mouse to select in
columns. columns.
* Selecting text automatically copies it to the primary clipboard (on * Selecting text automatically copies it to the primary clipboard (on platforms
platforms with a primary clipboard).
* You can middle click to paste from the primary clipboard (on platforms
with a primary clipboard). with a primary clipboard).
* You can right click while holding :kbd:`ctrl+shift` to open the output * You can middle click to paste from the primary clipboard (on platforms with a
of the clicked on command in a pager (requires :ref:`shell_integration`) primary clipboard).
* You can select text with kitty even when a terminal program has grabbed * You can right click while holding :kbd:`Ctrl+Shift` to open the output of the
the mouse by holding down the :kbd:`shift` key clicked on command in a pager (requires :ref:`shell_integration`)
* You can select text with kitty even when a terminal program has grabbed the
mouse by holding down the :kbd:`Shift` key
All these actions can be customized in :file:`kitty.conf` as described All these actions can be customized in :file:`kitty.conf` as described
:ref:`here <conf-kitty-mouse.mousemap>`. :ref:`here <conf-kitty-mouse.mousemap>`.
You can also customize what happens when clicking on :term:`hyperlinks` in kitty, You can also customize what happens when clicking on :term:`hyperlinks` in
having it open files in your editor, download remote files, open things kitty, having it open files in your editor, download remote files, open things
in your browser, etc. in your browser, etc.
For details, see :doc:`here <open_actions>`. For details, see :doc:`here <open_actions>`.
@@ -214,12 +223,11 @@ Font control
----------------- -----------------
|kitty| has extremely flexible and powerful font selection features. You can |kitty| has extremely flexible and powerful font selection features. You can
specify individual families for the regular, bold, italic and bold+italic specify individual families for the regular, bold, italic and bold+italic fonts.
fonts. You can even specify specific font families for specific ranges of You can even specify specific font families for specific ranges of Unicode
unicode characters. This allows precise control over text rendering. It can characters. This allows precise control over text rendering. It can comein handy
come in handy for applications like powerline, without the need to use patched for applications like powerline, without the need to use patched fonts. See the
fonts. See the various font related configuration directives in various font related configuration directives in :ref:`conf-kitty-fonts`.
:ref:`conf-kitty-fonts`.
.. _scrollback: .. _scrollback:
@@ -228,29 +236,29 @@ The scrollback buffer
----------------------- -----------------------
|kitty| supports scrolling back to view history, just like most terminals. You |kitty| supports scrolling back to view history, just like most terminals. You
can use either keyboard shortcuts or the mouse scroll wheel to do so. However, can use either keyboard shortcuts or the mouse scroll wheel to do so. However,
|kitty| has an extra, neat feature. Sometimes you need to explore the |kitty| has an extra, neat feature. Sometimes you need to explore the scrollback
scrollback buffer in more detail, maybe search for some text or refer to it buffer in more detail, maybe search for some text or refer to it side-by-side
side-by-side while typing in a follow-up command. |kitty| allows you to do this while typing in a follow-up command. |kitty| allows you to do this by pressing
by pressing the :sc:`show_scrollback` key-combination, which will open the the :sc:`show_scrollback` shortcut, which will open the scrollback buffer in
scrollback buffer in your favorite pager program (which is ``less`` by default). your favorite pager program (which is :program:`less` by default). Colors and
Colors and text formatting are preserved. You can explore the scrollback buffer text formatting are preserved. You can explore the scrollback buffer comfortably
comfortably within the pager. within the pager.
Additionally, you can pipe the contents of the scrollback buffer to an Additionally, you can pipe the contents of the scrollback buffer to an
arbitrary, command running in a new :term:`window`, :term:`tab` or :term:`overlay`, arbitrary, command running in a new :term:`window`, :term:`tab` or
for example:: :term:`overlay`. For example::
map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting less +G -R map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting less +G -R
Would open the scrollback buffer in a new :term:`window` when you press the :kbd:`F1` Would open the scrollback buffer in a new :term:`window` when you press the
key. See :sc:`show_scrollback <show_scrollback>` for details. :kbd:`F1` key. See :sc:`show_scrollback <show_scrollback>` for details.
If you want to use it with an editor such as vim to get more powerful features, If you want to use it with an editor such as :program:`vim` to get more powerful
you can see tips for doing so, in :iss:`this thread <719>`. features, you can see tips for doing so, in :iss:`this thread <719>`.
If you wish to store very large amounts of scrollback to view using the piping or If you wish to store very large amounts of scrollback to view using the piping
:sc:`show_scrollback <show_scrollback>` features, you can use the or :sc:`show_scrollback <show_scrollback>` features, you can use the
:opt:`scrollback_pager_history_size` option. :opt:`scrollback_pager_history_size` option.
@@ -258,8 +266,8 @@ Integration with shells
--------------------------------- ---------------------------------
kitty has the ability to integrate closely within common shells, such as `zsh kitty has the ability to integrate closely within common shells, such as `zsh
<https://www.zsh.org/>`_, `fish <https://fishshell.com>`_ and `bash <https://www.zsh.org/>`__, `fish <https://fishshell.com>`__ and `bash
<https://www.gnu.org/software/bash/>`_ to enable features such as jumping to <https://www.gnu.org/software/bash/>`__ to enable features such as jumping to
previous prompts in the scrollback, viewing the output of the last command in previous prompts in the scrollback, viewing the output of the last command in
:program:`less`, using the mouse to move the cursor while editing prompts, etc. :program:`less`, using the mouse to move the cursor while editing prompts, etc.
See :doc:`shell-integration` for details. See :doc:`shell-integration` for details.
@@ -274,17 +282,16 @@ See :doc:`shell-integration` for details.
Multiple copy/paste buffers Multiple copy/paste buffers
----------------------------- -----------------------------
In addition to being able to copy/paste from the system clipboard, in |kitty| you In addition to being able to copy/paste from the system clipboard, in |kitty|
can also setup an arbitrary number of copy paste buffers. To do so, simply add you can also setup an arbitrary number of copy paste buffers. To do so, simply
something like the following to your :file:`kitty.conf`:: add something like the following to your :file:`kitty.conf`::
map f1 copy_to_buffer a map f1 copy_to_buffer a
map f2 paste_from_buffer a map f2 paste_from_buffer a
This will allow you to press :kbd:`F1` to copy the current selection to an This will allow you to press :kbd:`F1` to copy the current selection to an
internal buffer named ``a`` and :kbd:`F2` to paste from that buffer. The buffer internal buffer named ``a`` and :kbd:`F2` to paste from that buffer. The buffer
names are arbitrary strings, so you can define as many such buffers as you names are arbitrary strings, so you can define as many such buffers as you need.
need.
Marks Marks

View File

@@ -2,30 +2,31 @@ Performance
=================== ===================
The main goals for |kitty| performance are user perceived latency while typing The main goals for |kitty| performance are user perceived latency while typing
and "smoothness" while scrolling as well as CPU usage. |kitty| tries hard to find and "smoothness" while scrolling as well as CPU usage. |kitty| tries hard to
an optimum balance for these. To that end it keeps a cache of each rendered find an optimum balance for these. To that end it keeps a cache of each rendered
glyph in video RAM so that font rendering is not a bottleneck. Interaction glyph in video RAM so that font rendering is not a bottleneck. Interaction with
with child programs takes place in a separate thread from rendering, to improve child programs takes place in a separate thread from rendering, to improve
smoothness. smoothness.
There are two parameters you can tune to adjust the performance. :opt:`repaint_delay` There are two config options you can tune to adjust the performance,
and :opt:`input_delay`. These control the artificial delays introduced into the :opt:`repaint_delay` and :opt:`input_delay`. These control the artificial delays
render loop to reduce CPU usage. See :ref:`conf-kitty-performance` for details. introduced into the render loop to reduce CPU usage. See
See also the :opt:`sync_to_monitor` option to further decrease latency at the cost :ref:`conf-kitty-performance` for details. See also the :opt:`sync_to_monitor`
of some `tearing <https://en.wikipedia.org/wiki/Screen_tearing>`_ while scrolling. option to further decrease latency at the cost of some `screen tearing
<https://en.wikipedia.org/wiki/Screen_tearing>`__ while scrolling.
You can generate detailed per-function performance data using `gperftools You can generate detailed per-function performance data using
<https://github.com/gperftools/gperftools>`_. Build |kitty| with `make `gperftools <https://github.com/gperftools/gperftools>`__. Build |kitty| with
profile`. Run kitty and perform the task you want to analyse, for example, ``make profile``. Run kitty and perform the task you want to analyse, for
scrolling a large file with `less`. After you quit, function call statistics example, scrolling a large file with :program:`less`. After you quit, function
will be printed to `stdout` and you can use tools like *kcachegrind* for more call statistics will be printed to STDOUT and you can use tools like
detailed analysis. *KCachegrind* for more detailed analysis.
Here are some CPU usage numbers for the task of scrolling a file continuously Here are some CPU usage numbers for the task of scrolling a file continuously in
in less. The CPU usage is for the terminal process and X together and is :program:`less`. The CPU usage is for the terminal process and X together and is
measured using htop. The measurements are taken at the same font and window measured using :program:`htop`. The measurements are taken at the same font and
size for all terminals on a ``Intel(R) Core(TM) i7-4820K CPU @ 3.70GHz`` CPU window size for all terminals on a ``Intel(R) Core(TM) i7-4820K CPU @ 3.70GHz``
with a ``Advanced Micro Devices, Inc. [AMD/ATI] Cape Verde XT [Radeon HD CPU with a ``Advanced Micro Devices, Inc. [AMD/ATI] Cape Verde XT [Radeon HD
7770/8760 / R7 250X]`` GPU. 7770/8760 / R7 250X]`` GPU.
============== ========================= ============== =========================
@@ -49,11 +50,11 @@ admittedly biased, eyes).
.. note:: .. note::
Some people have asked why kitty does not perform better than terminal XXX in Some people have asked why kitty does not perform better than terminal XXX
the test of sinking large amounts of data, such as catting a large text in the test of sinking large amounts of data, such as catting a large text
file. The answer is because this is not a goal for kitty. kitty file. The answer is because this is not a goal for kitty. kitty deliberately
deliberately throttles input parsing and output rendering to minimize throttles input parsing and output rendering to minimize resource usage
resource usage while still being able to sink output faster than any real while still being able to sink output faster than any real world program can
world program can produce it. Reducing CPU usage, and hence battery drain produce it. Reducing CPU usage, and hence battery drain while achieving
while achieving instant response times and smooth scrolling to a human eye instant response times and smooth scrolling to a human eye is a far more
is a far more important goal. important goal.

View File

@@ -1,23 +1,24 @@
Terminal protocol extensions Terminal protocol extensions
=================================== ===================================
|kitty| has extensions to the legacy terminal protocol, to enable advanced features. |kitty| has extensions to the legacy terminal protocol, to enable advanced
These are typically in the form of new or re-purposed escape codes. While these features. These are typically in the form of new or re-purposed escape codes.
extensions are currently |kitty| specific, it would be nice to get some of them While these extensions are currently |kitty| specific, it would be nice to get
adopted more broadly, to push the state of terminal emulators forward. some of them adopted more broadly, to push the state of terminal emulators
forward.
The goal of these extensions is to be as small and unobtrusive as possible, The goal of these extensions is to be as small and unobtrusive as possible,
while filling in some gaps in the existing xterm protocol. In particular, one while filling in some gaps in the existing xterm protocol. In particular, one of
of the goals of this specification is explicitly not to "re-imagine" the tty. the goals of this specification is explicitly not to "re-imagine" the TTY. The
The tty should remain what it is -- a device for efficiently processing text TTY should remain what it is -- a device for efficiently processing text
received as a simple byte stream. Another objective is to only move the minimum received as a simple byte stream. Another objective is to only move the minimum
possible amount of extra functionality into the terminal program itself. This possible amount of extra functionality into the terminal program itself. This is
is to make it as easy to implement these protocol extensions as possible, to make it as easy to implement these protocol extensions as possible, thereby
thereby hopefully encouraging their widespread adoption. hopefully encouraging their widespread adoption.
If you wish to discuss these extensions, propose additions/changes to them If you wish to discuss these extensions, propose additions or changes to them,
please do so by opening issues in the `GitHub please do so by opening issues in the `GitHub bug tracker
<https://github.com/kovidgoyal/kitty/issues>`_ bug tracker. <https://github.com/kovidgoyal/kitty/issues>`__.
.. toctree:: .. toctree::

View File

@@ -9,9 +9,9 @@ Quickstart
binary binary
build build
Pre-built binaries of |kitty| are available for both macOS and Linux. Pre-built binaries of |kitty| are available for both macOS and Linux. See the
See the :doc:`binary install instructions </binary>`. You can also :doc:`binary install instructions </binary>`. You can also :doc:`build from
:doc:`build from source </build>`. source </build>`.
Additionally, you can use your favorite package manager to install the |kitty| Additionally, you can use your favorite package manager to install the |kitty|
package, but note that some Linux distribution packages are woefully outdated. package, but note that some Linux distribution packages are woefully outdated.
@@ -25,4 +25,4 @@ and Linux.
See :doc:`Configuring kitty <conf>` for help on configuring |kitty| and See :doc:`Configuring kitty <conf>` for help on configuring |kitty| and
:doc:`Invocation <invocation>` for the command line arguments |kitty| supports. :doc:`Invocation <invocation>` for the command line arguments |kitty| supports.
For a tour of kitty's design and features, see the :doc:`/overview`. For a tour of kitty's design and features, see the :doc:`overview`.

View File

@@ -7,34 +7,74 @@ form::
<ESC>P@kitty-cmd<JSON object><ESC>\ <ESC>P@kitty-cmd<JSON object><ESC>\
Where ``<ESC>`` is the byte ``0x1b``. The JSON object has the form:: Where ``<ESC>`` is the byte ``0x1b``. The JSON object has the form:
.. code-block:: json
{ {
"cmd": "command name", "cmd": "command name",
"version": <kitty version>, "version": "<kitty version>",
"no_response": <Optional Boolean>, "no_response": "<Optional Boolean>",
"payload": <Optional JSON object>, "payload": "<Optional JSON object>"
} }
The ``version`` above is an array of the form :code:`[0, 14, 2]`. If you are developing a The ``version`` above is an array of the form :code:`[0, 14, 2]`. If you are
standalone client, use the kitty version that you are developing against. Using developing a standalone client, use the kitty version that you are developing
a version greater than the version of the kitty instance you are talking to, against. Using a version greater than the version of the kitty instance you are
will cause a failure. talking to, will cause a failure.
Set ``no_response`` to ``true`` if you don't want a response from kitty. Set ``no_response`` to ``true`` if you don't want a response from kitty.
The optional payload is a JSON object that is specific to the actual command being sent. The optional payload is a JSON object that is specific to the actual command
The fields in the object for every command are documented below. being sent. The fields in the object for every command are documented below.
As a quick example showing how easy to use this protocol is, we will implement As a quick example showing how easy to use this protocol is, we will implement
the ``@ ls`` command from the shell using only shell tools. First, run kitty the ``@ ls`` command from the shell using only shell tools.
as::
First, run kitty as::
kitty -o allow_remote_control=socket-only --listen-on unix:/tmp/test kitty -o allow_remote_control=socket-only --listen-on unix:/tmp/test
Now, in a different terminal, you can get the pretty printed ``@ ls`` output Now, in a different terminal, you can get the pretty printed ``@ ls`` output
with the following command line:: with the following command line::
echo -en '\eP@kitty-cmd{"cmd":"ls","version":[0,14,2]}\e\' | socat - unix:/tmp/test | awk '{ print substr($0, 13, length($0) - 14) }' | jq -c '.data | fromjson' | jq . echo -en '\eP@kitty-cmd{"cmd":"ls","version":[0,14,2]}\e\\' | socat - unix:/tmp/test | awk '{ print substr($0, 13, length($0) - 14) }' | jq -c '.data | fromjson' | jq .
.. _rc_crypto:
Encrypted communication
--------------------------
.. versionadded:: 0.26.0
When using the :opt:`remote_control_password` option communication to the
terminal is encrypted to keep the password secure. A public key is used from
the :envvar:`KITTY_PUBLIC_KEY` environment variable. Currently, only one
encryption protocol is supported. The protocol number is present in
:envvar:`KITTY_PUBLIC_KEY` as ``1``. The key data in this environment variable
is :rfc:`Base-85 <1924>` encoded. The algorithm used is `Elliptic Curve Diffie
Helman <https://en.wikipedia.org/wiki/Elliptic-curve_DiffieHellman>`__ with
the `X25519 curve <https://en.wikipedia.org/wiki/Curve25519>`__. A time based
nonce is used to minimise replay attacks. The original JSON command has the
fields: ``password`` and ``timestamp`` added. The timestamp is the number of
nanoseconds since the epoch, excluding leap seconds. Commands with a timestamp
more than 5 minutes from the current time are rejected. The command is then
encrypted using AES-256-GCM in authenticated encryption mode, with a symmetric
key that is derived from the ECDH key-pair by running the shared secret through
SHA-256 hashing, once. An IV of at least 96 bits of CSPRNG data is used. The
tag for authenticated encryption **must** be at least 128 bits long. The tag
**must** authenticate only the value of the ``encrypted`` field. A new command
is created and transmitted that contains the fields:
.. code-block:: json
{
"version": "<kitty version>",
"iv": "base85 encoded IV",
"tag": "base85 encoded AEAD tag",
"pubkey": "base85 encoded ECDH public key of sender",
"encrypted": "The original command encrypted and base85 encoded"
}
.. include:: generated/rc.rst .. include:: generated/rc.rst

View File

@@ -4,9 +4,12 @@ Control kitty from scripts
.. highlight:: sh .. highlight:: sh
|kitty| can be controlled from scripts or the shell prompt. You can open new |kitty| can be controlled from scripts or the shell prompt. You can open new
windows, send arbitrary text input to any window, name windows and tabs, etc. windows, send arbitrary text input to any window, change the title of windows
and tabs, etc.
Let's walk through a few examples of controlling |kitty|. Let's walk through a few examples of controlling |kitty|.
Tutorial Tutorial
------------ ------------
@@ -14,50 +17,52 @@ Start by running |kitty| as::
kitty -o allow_remote_control=yes -o enabled_layouts=tall kitty -o allow_remote_control=yes -o enabled_layouts=tall
In order for control to work, :opt:`allow_remote_control` must be enabled in In order for control to work, :opt:`allow_remote_control` or
:file:`kitty.conf`. Here we turn it on explicitly at the command line. :opt:`remote_control_password` must be enabled in :file:`kitty.conf`. Here we
turn it on explicitly at the command line.
Now, in the new |kitty| window, enter the command:: Now, in the new |kitty| window, enter the command::
kitty @ launch --title Output --keep-focus cat kitty @ launch --title Output --keep-focus cat
This will open a new window, running the ``cat`` program that will appear next This will open a new window, running the :program:`cat` program that will appear
to the current window. next to the current window.
Let's send some text to this new window:: Let's send some text to this new window::
kitty @ send-text --match cmdline:cat Hello, World kitty @ send-text --match cmdline:cat Hello, World
This will make ``Hello, World`` show up in the window running the ``cat`` program. This will make ``Hello, World`` show up in the window running the :program:`cat`
The :option:`kitty @ send-text --match` option is very powerful, it allows selecting windows by their program. The :option:`kitty @ send-text --match` option is very powerful, it
titles, the command line of the program running in the window, the working allows selecting windows by their titles, the command line of the program
directory of the program running in the window, etc. See ``kitty @ send-text running in the window, the working directory of the program running in the
--help`` for details. window, etc. See :ref:`kitty @ send-text --help <at-send-text>` for details.
More usefully, you can pipe the output of a command running in one window to More usefully, you can pipe the output of a command running in one window to
another window, for example:: another window, for example::
ls | kitty @ send-text --match title:Output --stdin ls | kitty @ send-text --match 'title:^Output' --stdin
This will show the output of ls in the output window instead of the current This will show the output of :program:`ls` in the output window instead of the
window. You can use this technique to, for example, show the output of running current window. You can use this technique to, for example, show the output of
``make`` in your editor in a different window. The possibilities are endless. running :program:`make` in your editor in a different window. The possibilities
are endless.
You can even have things you type show up in a different window. Run:: You can even have things you type show up in a different window. Run::
kitty @ send-text --match title:Output --stdin kitty @ send-text --match 'title:^Output' --stdin
And type some text, it will show up in the output window, instead of the current And type some text, it will show up in the output window, instead of the current
window. Type ``Ctrl+D`` when you are ready to stop. window. Type :kbd:`Ctrl+D` when you are ready to stop.
Now, let's open a new tab:: Now, let's open a new tab::
kitty @ launch --type=tab --tab-title "My Tab" --keep-focus bash kitty @ launch --type=tab --tab-title "My Tab" --keep-focus bash
This will open a new tab running the bash shell with the title "My Tab". This will open a new tab running the bash shell with the title "My Tab".
We can change the title of the tab with:: We can change the title of the tab to "New Title" with::
kitty @ set-tab-title --match title:My New Title kitty @ set-tab-title --match 'title:^My' New Title
Let's change the title of the current tab:: Let's change the title of the current tab::
@@ -65,79 +70,196 @@ Let's change the title of the current tab::
Now lets switch to the newly opened tab:: Now lets switch to the newly opened tab::
kitty @ focus-tab --match title:New kitty @ focus-tab --match 'title:^New'
Similarly, to focus the previously opened output window (which will also switch Similarly, to focus the previously opened output window (which will also switch
back to the old tab, automatically):: back to the old tab, automatically)::
kitty @ focus-window --match title:Output kitty @ focus-window --match 'title:^Output'
You can get a listing of available tabs and windows, by running:: You can get a listing of available tabs and windows, by running::
kitty @ ls kitty @ ls
This outputs a tree of data in JSON format. The top level of the tree is all This outputs a tree of data in JSON format. The top level of the tree is all
operating system kitty windows. Each OS window has an id and a list of tabs. :term:`OS windows <os_window>`. Each OS window has an id and a list of
Each tab has its own id, a title and a list of windows. Each window has an id, :term:`tabs <tab>`. Each tab has its own id, a title and a list of :term:`kitty
title, current working directory, process id (PID) and command-line of the windows <window>`. Each window has an id, title, current working directory,
process running in the window. You can use this information with :option:`kitty @ focus-window --match` process id (PID) and command-line of the process running in the window. You can
to control individual windows. use this information with :option:`kitty @ focus-window --match` to control
individual windows.
As you can see, it is very easy to control |kitty| using the As you can see, it is very easy to control |kitty| using the ``kitty @``
``kitty @`` messaging system. This tutorial touches only the messaging system. This tutorial touches only the surface of what is possible.
surface of what is possible. See ``kitty @ --help`` for more details. See ``kitty @ --help`` for more details.
Note that in the example's above, ``kitty @`` messaging works only when run inside a |kitty| window, In the example's above, ``kitty @`` messaging works only when run
not anywhere. But, within a |kitty| window it even works over SSH. If you want to control inside a |kitty| window, not anywhere. But, within a |kitty| window it even
|kitty| from programs/scripts not running inside a |kitty| window, you have to implement a couple of works over SSH. If you want to control |kitty| from programs/scripts not running
extra steps. First start |kitty| as:: inside a |kitty| window, see the section on :ref:`using a socket for remote control <rc_via_socket>`
below.
Note that if all you want to do is run a single |kitty| "daemon" and have
subsequent |kitty| invocations appear as new top-level windows, you can use the
simpler :option:`kitty --single-instance` option, see ``kitty --help`` for that.
.. _rc_via_socket:
Remote control via a socket
--------------------------------
First, start |kitty| as::
kitty -o allow_remote_control=yes --listen-on unix:/tmp/mykitty kitty -o allow_remote_control=yes --listen-on unix:/tmp/mykitty
The :option:`kitty --listen-on` option tells |kitty| to listen for control messages at the The :option:`kitty --listen-on` option tells |kitty| to listen for control
specified path. See ``kitty --help`` for details. Now you can control this messages at the specified UNIX-domain socket. See ``kitty --help`` for details.
instance of |kitty| using the :option:`kitty @ --to` command line argument to ``kitty @``. For example:: Now you can control this instance of |kitty| using the :option:`kitty @ --to`
command line argument to ``kitty @``. For example::
kitty @ --to unix:/tmp/mykitty ls kitty @ --to unix:/tmp/mykitty ls
Note that if all you want to do is run a single |kitty| "daemon" and have subsequent
|kitty| invocations appear as new top-level windows, you can use the simpler :option:`kitty --single-instance`
option, see ``kitty --help`` for that.
The builtin kitty shell The builtin kitty shell
-------------------------- --------------------------
You can explore the |kitty| command language more easily using the builtin |kitty| You can explore the |kitty| command language more easily using the builtin
shell. Run ``kitty @`` with no arguments and you will be dropped into the |kitty| |kitty| shell. Run ``kitty @`` with no arguments and you will be dropped into
shell with completion for |kitty| command names and options. the |kitty| shell with completion for |kitty| command names and options.
You can even open the |kitty| shell inside a running |kitty| using a simple You can even open the |kitty| shell inside a running |kitty| using a simple
keyboard shortcut (:sc:`kitty_shell` by default). keyboard shortcut (:sc:`kitty_shell` by default).
.. note:: This has the added advantage that you don't need to use .. note:: This has the added advantage that you don't need to use
``allow_remote_control`` to make it work. :opt:`allow_remote_control` to make it work.
Allowing only some windows to control kitty Allowing only some windows to control kitty
---------------------------------------------- ----------------------------------------------
If you do not want to allow all programs running in |kitty| to control it, you can selectively If you do not want to allow all programs running in |kitty| to control it, you
enable remote control for only some |kitty| windows. Simply create a shortcut can selectively enable remote control for only some |kitty| windows. Simply
such as:: create a shortcut such as::
map ctrl+k launch --allow-remote-control some_program map ctrl+k launch --allow-remote-control some_program
Then programs running in windows created with that shortcut can use ``kitty @`` Then programs running in windows created with that shortcut can use ``kitty @``
to control kitty. Note that any program with the right level of permissions can to control kitty. Note that any program with the right level of permissions can
still write to the pipes of any other program on the same computer and still write to the pipes of any other program on the same computer and therefore
therefore can control |kitty|. It can, however, be useful to block programs can control |kitty|. It can, however, be useful to block programs running on
running on other computers (for example, over ssh) or as other users. other computers (for example, over SSH) or as other users.
.. note:: You dont need ``allow_remote_control`` to make this work as it is .. note:: You don't need :opt:`allow_remote_control` to make this work as it is
limited to only programs running in that specific window. Be careful with limited to only programs running in that specific window. Be careful with
what programs you run in such windows, since they can effectively control what programs you run in such windows, since they can effectively control
kitty, as if you were running with ``allow_remote_control`` turned on. kitty, as if you were running with :opt:`allow_remote_control` turned on.
You can further restrict what is allowed in these windows by using
:option:`kitty @ launch --remote-control-password`.
Fine grained permissions for remote control
----------------------------------------------
.. versionadded:: 0.26.0
The :opt:`allow_remote_control` option discussed so far is a blunt
instrument, granting the ability to any program running on your computer
or even on remote computers via SSH the ability to use remote control.
You can instead define remote control passwords that can be used to grant
different levels of control to different places. You can even write your
own script to decide which remote control requests are allowed. This is
done using the :opt:`remote_control_password` option in :file:`kitty.conf`.
Set :opt:`allow_remote_control` to :code:`password` to use this feature.
Let's see some examples:
.. code-block:: conf
remote_control_password "control colors" get-colors set-colors
Now, using this password, you can, in scripts run the command::
kitty @ --password="control colors" set-colors background=red
Any script with access to the password can now change colors in kitty using
remote control, but only that and nothing else. You can even supply the
password via the :envvar:`KITTY_RC_PASSWORD` environment variable, or the
file :file:`~/.config/kitty/rc-password` to avoid having to type it repeatedly.
See :option:`kitty @ --password-file` and :option:`kitty @ --password-env`.
The :opt:`remote_control_password` can be specified multiple times to create
different passwords with different capabilities. Run the following to get a
list of all action names::
kitty @ --help
You can even use glob patterns to match action names, for example:
.. code-block:: conf
remote_control_password "control colors" *-colors
If no action names are specified, all actions are allowed.
If ``kitty @`` is run with a password that is not present in
:file:`kitty.conf`, then kitty will interactively prompt the user to allow or
disallow the remote control request. The user can choose to allow or disallow
either just that request or all requests using that password. The user's
decision is remembered for the duration of that kitty instance.
.. note::
For password based authentication to work over SSH, you must pass the
:envvar:`KITTY_PUBLIC_KEY` environment variable to the remote host. The
:doc:`ssh kitten <kittens/ssh>` does this for you automatically. When
using a password, :ref:`rc_crypto` is used to ensure the password
is kept secure. This does mean that using password based authentication
is slower as the entire command is encrypted before transmission. This
can be noticeable when using a command like ``kitty @ set-background-image``
which transmits large amounts of image data. Also, the clock on the remote
system must match (within a few minutes) the clock on the local system.
kitty uses a time based nonce to minimise the potential for replay attacks.
.. _rc_custom_auth:
Customizing authorization with your own program
____________________________________________________________
If the ability to control access by action names is not fine grained enough,
you can define your own Python script to examine every remote control command
and allow/disallow it. To do so create a file in the kitty configuration
directory, :file:`~/.config/kitty/my_rc_auth.py` and add the following
to :file:`kitty.conf`:
.. code-block:: conf
remote_control_password "testing custom auth" my_rc_auth.py
:file:`my_rc_auth.py` should define a :code:`is_cmd_allowed` function
as shown below:
.. code-block:: py
def is_cmd_allowed(pcmd, window, from_socket, extra_data):
cmd_name = pcmd['cmd'] # the name of the command
cmd_payload = pcmd['payload'] # the arguments to the command
# examine the cmd_name and cmd_payload and return True to allow
# the command or False to disallow it. Return None to have no
# effect on the command.
# The command payload will vary from command to command, see
# the rc protocol docs for details. Below is an example of
# restricting the launch command to allow only running the
# default shell.
if cmd_name != 'launch':
return None
if cmd_payload.get('args') or cmd_payload.get('env') or cmd_payload.get('copy_cmdline') or cmd_payload.get('copy_env'):
return False
# prints in this function go to the parent kitty process STDOUT
print('Allowing launch command:', cmd_payload)
return True
.. _rc_mapping: .. _rc_mapping:
@@ -148,15 +270,21 @@ Mapping key presses to remote control commands
If you wish to trigger a remote control command easily with just a keypress, If you wish to trigger a remote control command easily with just a keypress,
you can map it in :file:`kitty.conf`. For example:: you can map it in :file:`kitty.conf`. For example::
map F1 remote_control set-spacing margin=30 map f1 remote_control set-spacing margin=30
Then pressing the :kbd:`F1` key will set the active window margins to 30. Then pressing the :kbd:`F1` key will set the active window margins to
The syntax for what follows :code:`remote_control` is exactly the same :code:`30`. The syntax for what follows :ac:`remote_control` is exactly the same
as the syntax for what follows :code:`kitty @` above. as the syntax for what follows :code:`kitty @` above.
.. note:: You do not need ``allow_remote_control`` to use these mappings, If you wish to ignore errors from the command, prefix the command with an
as they are not actual remote programs, but are simply a way to resuse ``!``. For example, the following will not return an error when no windows
the remote control infrastructure via keybings. are matched::
map f1 remote_control !focus-window --match XXXXXX
.. note:: You do not need :opt:`allow_remote_control` to use these mappings,
as they are not actual remote programs, but are simply a way to resuse the
remote control infrastructure via keybings.
Broadcasting what you type to all kitty windows Broadcasting what you type to all kitty windows
@@ -166,17 +294,36 @@ As a simple illustration of the power of remote control, lets
have what we type sent to all open kitty windows. To do that define the have what we type sent to all open kitty windows. To do that define the
following mapping in :file:`kitty.conf`:: following mapping in :file:`kitty.conf`::
map F1 launch --allow-remote-control kitty +kitten broadcast map f1 launch --allow-remote-control kitty +kitten broadcast
Now press, F1 and start typing, what you type will be sent to all windows, Now press :kbd:`F1` and start typing, what you type will be sent to all windows,
live, as you type it. live, as you type it.
The remote control protocol The remote control protocol
----------------------------------------------- -----------------------------------------------
If you wish to develop your own client to talk to |kitty|, you If you wish to develop your own client to talk to |kitty|, you can use the
can use the :doc:`protocol specification <rc_protocol>`. :doc:`remote control protocol specification <rc_protocol>`.
.. _search_syntax:
Matching windows and tabs
----------------------------
Many remote control operations operate on windows or tabs. To select these, the
:code:`--match` option is often used. This allows matching using various
sophisticated criteria such as title, ids, cmdlines, etc. These criteria are
expressions of the form :code:`field:query`. Where :italic:`field` is the field
against which to match and :italic:`query` is the expression to match. They can
be further combined using Boolean operators, best illustrated with some
examples::
title:"My special window" or id:43
title:bash and env:USER=kovid
not id:1
(id:2 or id:3) and title:something
.. toctree:: .. toctree::
:hidden: :hidden:

View File

@@ -4,8 +4,8 @@ Shell integration
------------------- -------------------
kitty has the ability to integrate closely within common shells, such as `zsh kitty has the ability to integrate closely within common shells, such as `zsh
<https://www.zsh.org/>`_, `fish <https://fishshell.com>`_ and `bash <https://www.zsh.org/>`__, `fish <https://fishshell.com>`__ and `bash
<https://www.gnu.org/software/bash/>`_ to enable features such as jumping to <https://www.gnu.org/software/bash/>`__ to enable features such as jumping to
previous prompts in the scrollback, viewing the output of the last command in previous prompts in the scrollback, viewing the output of the last command in
:program:`less`, using the mouse to move the cursor while editing prompts, etc. :program:`less`, using the mouse to move the cursor while editing prompts, etc.
@@ -22,7 +22,7 @@ Features
* Click with the mouse anywhere in the current command to move the cursor there * Click with the mouse anywhere in the current command to move the cursor there
* Hold :kbd:`ctrl+shift` and right-click on any command output in the scrollback * Hold :kbd:`Ctrl+Shift` and right-click on any command output in the scrollback
to view it in a pager to view it in a pager
* The current working directory or the command being executed are automatically * The current working directory or the command being executed are automatically
@@ -30,20 +30,25 @@ Features
* The text cursor is changed to a bar when editing commands at the shell prompt * The text cursor is changed to a bar when editing commands at the shell prompt
* :ref:`clone_shell` with all environment variables and the working directory
copied
* :ref:`Edit files in new kitty windows <edit_file>` even over SSH
* Glitch free window resizing even with complex prompts. Achieved by erasing * Glitch free window resizing even with complex prompts. Achieved by erasing
the prompt on resize and allowing the shell to redraw it cleanly. the prompt on resize and allowing the shell to redraw it cleanly.
* Sophisticated completion for the :program:`kitty` command in the shell. * Sophisticated completion for the :program:`kitty` command in the shell.
* When confirming a quit command if a window is sitting at a shell prompt, * When confirming a quit command if a window is sitting at a shell prompt,
it is optionally, not counted (see :opt:`confirm_os_window_close`) it is not counted (for details, see :opt:`confirm_os_window_close`)
Configuration Configuration
--------------- ---------------
Shell integration is controlled by the :opt:`shell_integration` option. By Shell integration is controlled by the :opt:`shell_integration` option. By
default, all shell integration is enabled. Individual features can be turned default, all integration features are enabled. Individual features can be turned
off or it can be disabled entirely as well. The :opt:`shell_integration` option off or it can be disabled entirely as well. The :opt:`shell_integration` option
takes a space separated list of keywords: takes a space separated list of keywords:
@@ -53,19 +58,29 @@ disabled
:ref:`manual integration <manual_shell_integration>`. :ref:`manual integration <manual_shell_integration>`.
no-rc no-rc
Do not modify the shell's launch environment to enable integration. Useful if you prefer Do not modify the shell's launch environment to enable integration. Useful
to load the kitty shell integration code yourself, either as part of if you prefer to load the kitty shell integration code yourself, either as
:ref:`manually integration <manual_shell_integration>` or because you have part of :ref:`manually integration <manual_shell_integration>` or because
some other software that sets up shell integration. you have some other software that sets up shell integration.
This will still set the :envvar:`KITTY_SHELL_INTEGRATION` environment variable when kitty runs the shell. This will still set the :envvar:`KITTY_SHELL_INTEGRATION` environment
variable when kitty runs the shell.
no-cursor no-cursor
Turn off changing of the text cursor to a bar when editing text Turn off changing of the text cursor to a bar when editing shell command
line.
no-title no-title
Turn off setting the kitty window/tab title based on shell state. Turn off setting the kitty window/tab title based on shell state.
Note that for the ``fish`` shell kitty relies on fish's native title Note that for the fish shell kitty relies on fish's native title setting
setting functionality instead. functionality instead.
no-cwd
Turn off reporting the current working directory. This is used to allow
:ac:`new_window_with_cwd` and similar to open windows logged into remote
machines using the :doc:`ssh kitten <kittens/ssh>` automatically with the
same working directory as the current window.
Note that for the fish shell this will not disable its built-in current
working directory reporting.
no-prompt-mark no-prompt-mark
Turn off marking of prompts. This disables jumping to prompt, browsing Turn off marking of prompts. This disables jumping to prompt, browsing
@@ -73,16 +88,16 @@ no-prompt-mark
no-complete no-complete
Turn off completion for the kitty command. Turn off completion for the kitty command.
Note that for the ``fish`` shell this does not take effect, since fish Note that for the fish shell this does not take effect, since fish already
already comes with a kitty completion script. comes with a kitty completion script.
More ways to browse command output More ways to browse command output
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can add further key and mouse bindings to browse the output of commands You can add further key and mouse bindings to browse the output of commands
easily. For example to select the output of a command by right clicking the mouse easily. For example to select the output of a command by right clicking the
on the output, define the following in :file:`kitty.conf`: mouse on the output, define the following in :file:`kitty.conf`:
.. code:: conf .. code:: conf
@@ -93,8 +108,8 @@ to be copied.
The feature to jump to previous prompts ( The feature to jump to previous prompts (
:sc:`scroll_to_previous_prompt` and :sc:`scroll_to_next_prompt`) and mouse :sc:`scroll_to_previous_prompt` and :sc:`scroll_to_next_prompt`) and mouse
actions (:ac:`mouse_select_command_output` and :ac:`mouse_show_command_output`) can actions (:ac:`mouse_select_command_output` and :ac:`mouse_show_command_output`)
be integrated with browsing command output as well. For example, define the can be integrated with browsing command output as well. For example, define the
following mapping in :file:`kitty.conf`: following mapping in :file:`kitty.conf`:
.. code:: conf .. code:: conf
@@ -126,42 +141,41 @@ How it works
----------------- -----------------
At startup, kitty detects if the shell you have configured (either system wide At startup, kitty detects if the shell you have configured (either system wide
or in kitty.conf) is a supported shell. If so, kitty injects some shell specific or the :opt:`shell` option in :file:`kitty.conf`) is a supported shell. If so,
code into the shell, to enable shell integration. How it does so varies for kitty injects some shell specific code into the shell, to enable shell
different shells. integration. How it does so varies for different shells.
.. tab:: zsh .. tab:: zsh
For zsh, kitty sets the ``ZDOTDIR`` environment variable to make zsh load For zsh, kitty sets the :envvar:`ZDOTDIR` environment variable to make zsh
kitty's :file:`.zshenv` which restores the original value of ``ZDOTDIR`` load kitty's :file:`.zshenv` which restores the original value of
and sources the original :file:`.zshenv`. It then loads the shell integration code. :envvar:`ZDOTDIR` and sources the original :file:`.zshenv`. It then loads
The remainder of zsh's startup process proceeds as normal. the shell integration code. The remainder of zsh's startup process proceeds
as normal.
.. tab:: fish .. tab:: fish
For fish, to make it automatically load the integration code provided by For fish, to make it automatically load the integration code provided by
kitty, the integration script directory path is prepended to the kitty, the integration script directory path is prepended to the
``XDG_DATA_DIRS`` environment variable. This is only applied to the fish :envvar:`XDG_DATA_DIRS` environment variable. This is only applied to the
process and will be cleaned up by the integration script after startup. No files fish process and will be cleaned up by the integration script after startup.
are added or modified. No files are added or modified.
.. tab:: bash .. tab:: bash
For bash, kitty starts bash in POSIX mode, using the environment variable For bash, kitty starts bash in POSIX mode, using the environment variable
``ENV`` to load the shell integrtion script. This prevents bash from :envvar:`ENV` to load the shell integration script. This prevents bash from
loading any startup files itself. The loading of the startup files is done loading any startup files itself. The loading of the startup files is done
by the integration script, after disabling POSIX mode. From the by the integration script, after disabling POSIX mode. From the perspective
perspective of those scripts there should be no difference to running of those scripts there should be no difference to running vanilla bash.
vanilla bash.
Then, when launching the shell, kitty sets the environment variable Then, when launching the shell, kitty sets the environment variable
:envvar:`KITTY_SHELL_INTEGRATION` to the value of the :opt:`shell_integration` :envvar:`KITTY_SHELL_INTEGRATION` to the value of the :opt:`shell_integration`
option. The shell integration code reads the environment variable, turns on the option. The shell integration code reads the environment variable, turns on the
specified integration functionality and then unsets the variable so as to not specified integration functionality and then unsets the variable so as to not
pollute the system. This has the nice effect that the changes to the shell's rc pollute the system.
files become no-ops when running the shell in anything other than kitty itself.
The actual shell integration code uses hooks provided by each shell to send The actual shell integration code uses hooks provided by each shell to send
special escape codes to kitty, to perform the various tasks. You can see the special escape codes to kitty, to perform the various tasks. You can see the
@@ -194,13 +208,97 @@ code used for each shell below:
</details> </details>
Shell integration over SSH
----------------------------
The easiest way to have shell integration work when SSHing into remote systems
is to use the :doc:`ssh kitten <kittens/ssh>`. Simply run::
kitty +kitten ssh hostname
And, by magic, you will be logged into the remote system with fully functional
shell integration. Alternately, you can :ref:`setup shell integration manually
<manual_shell_integration>`, by copying the kitty shell integration scripts to
the remote server and editing the shell rc files there, as described below.
.. _clone_shell:
Clone the current shell into a new window
-----------------------------------------------
You can clone the current shell into a new kitty window by simply running the
:command:`clone-in-kitty` command, for example:
.. code-block:: sh
clone-in-kitty
clone-in-kitty --type=tab
clone-in-kitty --title "I am a clone"
This will open a new window running a new shell instance but with all
environment variables and the current working directory copied. This even works
over SSH when using :doc:`kittens/ssh`.
The :command:`clone-in-kitty` command takes almost all the same arguments as the
:doc:`launch <launch>` command, so you can open a new tab instead or a new OS
window, etc. Arguments of launch that can cause code execution or that don't
make sense when cloning are ignored. Most prominently, the following options are
ignored: :option:`--allow-remote-control <launch --allow-remote-control>`,
:option:`--copy-cmdline <launch --copy-cmdline>`, :option:`--copy-env <launch
--copy-env>`, :option:`--stdin-source <launch --stdin-source>`,
:option:`--marker <launch --marker>` and :option:`--watcher <launch --watcher>`.
:command:`clone-in-kitty` can be configured to source arbitrary code in the
cloned window using environment variables. It will automatically clone virtual
environments created by the :link:`Python venv module
<https://docs.python.org/3/library/venv.html>` or :link:`Conda
<https://conda.io/>`. In addition, setting the
env var :envvar:`KITTY_CLONE_SOURCE_CODE` to some shell code will cause that
code to be run in the cloned window with :code:`eval`. Similarly, setting
:envvar:`KITTY_CLONE_SOURCE_PATH` to the path of a file will cause that file to
be sourced in the cloned window. This can be controlled by
:opt:`clone_source_strategies`.
:command:`clone-in-kitty` works by asking the shell to serialize its internal
state (mainly CWD and env vars) and this state is transmitted to kitty and
restored by the shell integration scripts in the cloned window.
.. _edit_file:
Edit files in new kitty windows even over SSH
------------------------------------------------
.. code-block:: sh
edit-in-kitty myfile.txt
edit-in-kitty --type tab --title "Editing My File" myfile.txt
# open myfile.txt at line 75 (works with vim, neovim, emacs, nano, micro)
edit-in-kitty +75 myfile.txt
The :command:`edit-in-kitty` command allows you to seamlessly edit files
in your default :opt:`editor` in new kitty windows. This works even over
SSH (if you use the :doc:`ssh kitten <kittens/ssh>`), allowing you
to easily edit remote files in your local editor with all its bells and
whistles.
The :command:`edit-in-kitty` command takes almost all the same arguments as the
:doc:`launch <launch>` command, so you can open a new tab instead or a new OS
window, etc. Not all arguments are supported, see the discussion in the
:ref:`clone_shell` section above.
In order to avoid remote code execution, kitty will only execute the configured
editor and pass the file path to edit to it.
.. _manual_shell_integration: .. _manual_shell_integration:
Manual shell integration Manual shell integration
---------------------------- ----------------------------
The automatic shell integration is designed to be minimally intrusive, as such The automatic shell integration is designed to be minimally intrusive, as such
it wont work for sub-shells, terminal multiplexers, containers, remote systems, etc. it wont work for sub-shells, terminal multiplexers, containers, etc.
For such systems, you should setup manual shell integration by adding some code For such systems, you should setup manual shell integration by adding some code
to your shells startup files to load the shell integration script. to your shells startup files to load the shell integration script.
@@ -248,18 +346,17 @@ The value of :envvar:`KITTY_SHELL_INTEGRATION` is the same as that for
completely, in which case simply do not set the completely, in which case simply do not set the
:envvar:`KITTY_SHELL_INTEGRATION` variable at all. :envvar:`KITTY_SHELL_INTEGRATION` variable at all.
If you want this to work while SSHing into a remote system, then you will In a container, you will need to install the kitty shell integration scripts
need to add some code to the snippets above to check if :envvar:`KITTY_INSTALLATION_DIR` and make sure the :envvar:`KITTY_INSTALLATION_DIR` environment variable is set
is empty and if so to set it to some hard coded location with the shell to point to the location of the scripts.
integration scripts that need to be copied onto the remote system.
Integration with other shells Integration with other shells
------------------------------- -------------------------------
There exist third-party integrations to use these features for various other shells: There exist third-party integrations to use these features for various other
shells:
* Jupyter console via a patch (:iss:`4475`) * Jupyter console and IPython via a patch (:iss:`4475`)
* `xonsh <https://github.com/xonsh/xonsh/issues/4623>`__ * `xonsh <https://github.com/xonsh/xonsh/issues/4623>`__

View File

@@ -1,24 +1,25 @@
Colored and styled underlines Colored and styled underlines
================================ ================================
|kitty| supports colored and styled (wavy) underlines. This is of particular |kitty| supports colored and styled (wavy) underlines. This is of particular use
use in terminal editors such as vim and emacs to display red, wavy underlines in terminal based text editors such as :program:`vim` and :program:`emacs` to
under mis-spelled words and/or syntax errors. This is done by re-purposing some display red, wavy underlines under mis-spelled words and/or syntax errors. This
SGR escape codes that are not used in modern terminals (`CSI codes is done by re-purposing some SGR escape codes that are not used in modern
<https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences>`_) terminals (`CSI codes <https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_(Control_Sequence_Introducer)_sequences>`__)
To set the underline style:: To set the underline style::
<ESC>[4:0m # this is no underline <ESC>[4:0m # no underline
<ESC>[4:1m # this is a straight underline <ESC>[4:1m # straight underline
<ESC>[4:2m # this is a double underline <ESC>[4:2m # double underline
<ESC>[4:3m # this is a curly underline <ESC>[4:3m # curly underline
<ESC>[4:4m # this is a dotted underline <ESC>[4:4m # dotted underline
<ESC>[4:5m # this is a dashed underline <ESC>[4:5m # dashed underline
<ESC>[4m # this is a straight underline (for backwards compat) <ESC>[4m # straight underline (for backwards compat)
<ESC>[24m # this is no underline (for backwards compat) <ESC>[24m # no underline (for backwards compat)
To set the underline color (this is reserved and as far as I can tell not actually used for anything):: To set the underline color (this is reserved and as far as I can tell not
actually used for anything)::
<ESC>[58...m <ESC>[58...m
@@ -29,8 +30,8 @@ To reset the underline color (also previously reserved and unused)::
<ESC>[59m <ESC>[59m
The underline color must remain the same under reverse video, if it has a The underline color must remain the same under reverse video, if it has a color,
color, if not, it should follow the foreground color. if not, it should follow the foreground color.
To detect support for this feature in a terminal emulator, query the terminfo database To detect support for this feature in a terminal emulator, query the terminfo
for the ``Su`` boolean capability. database for the ``Su`` boolean capability.

View File

@@ -4,10 +4,10 @@ Unscrolling the screen
======================== ========================
This is a small extension to the `SD (Pan up) escape code This is a small extension to the `SD (Pan up) escape code
<https://vt100.net/docs/vt510-rm/SD.html>`_ from the VT-420 terminal. The <https://vt100.net/docs/vt510-rm/SD.html>`_ from the VT-420 terminal. The ``SD``
``SD`` escape code normally causes the text on screen to scroll down by the escape code normally causes the text on screen to scroll down by the specified
specified number of lines, with empty lines appearing at the top of the screen. number of lines, with empty lines appearing at the top of the screen. This
This extension allows the new lines to be filled in from the scrollback buffer extension allows the new lines to be filled in from the scrollback buffer
instead of being blank. instead of being blank.
The motivation for this is that many modern shells will show completions in a The motivation for this is that many modern shells will show completions in a
@@ -22,17 +22,17 @@ scrolled down is implementation defined, but must be at least one screen worth.
The syntax of the escape code is identical to that of ``SD`` except that it has The syntax of the escape code is identical to that of ``SD`` except that it has
a trailing ``+`` modifier. This is legal under the `ECMA 48 standard a trailing ``+`` modifier. This is legal under the `ECMA 48 standard
<https://www.ecma-international.org/publications-and-standards/standards/ecma-48/>`_ <https://www.ecma-international.org/publications-and-standards/standards/ecma-48/>`__
and unused for any other purpose as far as I can tell. So for example, to and unused for any other purpose as far as I can tell. So for example, to
unscroll three lines, the escape code would be:: unscroll three lines, the escape code would be::
CSI 3 + T CSI 3 + T
See `discussion here See `discussion here
<https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/30>`_. <https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/30>`__.
.. versionadded:: 0.20.2 .. versionadded:: 0.20.2
Also supported by the terminals: Also supported by the terminals:
* `mintty <https://github.com/mintty/mintty/releases/tag/3.5.2>`_ * `mintty <https://github.com/mintty/mintty/releases/tag/3.5.2>`__

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!./kitty/launcher/kitty +launch
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net> # License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
@@ -38,6 +38,8 @@ def main() -> None:
from kittens.diff.options.definition import definition as kd from kittens.diff.options.definition import definition as kd
write_output('kittens.diff', kd) write_output('kittens.diff', kd)
from kittens.ssh.options.definition import definition as sd
write_output('kittens.ssh', sd)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -770,6 +770,11 @@ is_useful_apple_global_shortcut(int sc) {
} }
} }
static bool
is_apple_jis_layout_function_key(NSEvent *event) {
return [event keyCode] == 0x66 /* kVK_JIS_Eisu */ || [event keyCode] == 0x68 /* kVK_JIS_Kana */;
}
GLFWAPI GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback) { GLFWAPI GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback) {
GLFWapplicationshouldhandlereopenfun previous = handle_reopen_callback; GLFWapplicationshouldhandlereopenfun previous = handle_reopen_callback;
handle_reopen_callback = callback; handle_reopen_callback = callback;
@@ -837,6 +842,14 @@ int _glfwPlatformInit(void)
last_keydown_shortcut_event.timestamp = [event timestamp]; last_keydown_shortcut_event.timestamp = [event timestamp];
return event; return event;
} }
// check for JIS keyboard layout function keys
if (is_apple_jis_layout_function_key(event)) {
debug_key("keyDown triggerred JIS layout function key ignoring\n");
last_keydown_shortcut_event.virtual_key_code = [event keyCode];
last_keydown_shortcut_event.input_source_switch_modifiers = 0;
last_keydown_shortcut_event.timestamp = [event timestamp];
return event;
}
} }
last_keydown_shortcut_event.virtual_key_code = 0xffff; last_keydown_shortcut_event.virtual_key_code = 0xffff;
NSWindow *kw = [NSApp keyWindow]; NSWindow *kw = [NSApp keyWindow];

View File

@@ -29,6 +29,7 @@
#include "internal.h" #include "internal.h"
#include "../kitty/monotonic.h" #include "../kitty/monotonic.h"
#include <Availability.h>
#include <float.h> #include <float.h>
#include <string.h> #include <string.h>
@@ -1377,7 +1378,7 @@ is_ascii_control_char(char x) {
if ([obj isKindOfClass:[NSURL class]]) { if ([obj isKindOfClass:[NSURL class]]) {
NSURL *url = (NSURL*)obj; NSURL *url = (NSURL*)obj;
if ([uri_list length] > 0) [uri_list appendString:@("\n")]; if ([uri_list length] > 0) [uri_list appendString:@("\n")];
if (url.fileURL) [uri_list appendFormat:@("file://%s"), url.fileSystemRepresentation]; if (url.fileURL) [uri_list appendString:url.filePathURL.absoluteString];
else [uri_list appendString:url.absoluteString]; else [uri_list appendString:url.absoluteString];
} else if ([obj isKindOfClass:[NSString class]]) { } else if ([obj isKindOfClass:[NSString class]]) {
const char *text = [obj UTF8String]; const char *text = [obj UTF8String];
@@ -1540,6 +1541,39 @@ void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
debug_key("\n\tdoCommandBySelector: (%s)\n", [NSStringFromSelector(selector) UTF8String]); debug_key("\n\tdoCommandBySelector: (%s)\n", [NSStringFromSelector(selector) UTF8String]);
} }
- (BOOL)isAccessibilityElement
{
return YES;
}
- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector
{
if (selector == @selector(accessibilityRole) || selector == @selector(accessibilitySelectedText)) return YES;
return NO;
}
#if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400)
- (NSAccessibilityRole)accessibilityRole
{
return NSAccessibilityTextAreaRole;
}
#endif
- (NSString *)accessibilitySelectedText
{
NSString *text = nil;
if (_glfw.callbacks.get_current_selection) {
char *s = _glfw.callbacks.get_current_selection();
if (s) {
text = [NSString stringWithUTF8String:s];
free(s);
}
}
return text;
}
@end @end
// }}} // }}}
@@ -1731,7 +1765,11 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
if (!createNativeWindow(window, wndconfig, fbconfig)) if (!createNativeWindow(window, wndconfig, fbconfig))
return false; return false;
[window->ns.object setColorSpace:[NSColorSpace sRGBColorSpace]]; switch((GlfwCocoaColorSpaces)wndconfig->ns.color_space) {
case SRGB_COLORSPACE: [window->ns.object setColorSpace:[NSColorSpace sRGBColorSpace]]; break;
case DISPLAY_P3_COLORSPACE: [window->ns.object setColorSpace:[NSColorSpace displayP3ColorSpace]]; break;
case DEFAULT_COLORSPACE: break;
}
if (ctxconfig->client != GLFW_NO_API) if (ctxconfig->client != GLFW_NO_API)
{ {
@@ -2475,28 +2513,45 @@ void _glfwPlatformSetClipboardString(const char* string)
[pasteboard setString:@(string) forType:NSPasteboardTypeString]; [pasteboard setString:@(string) forType:NSPasteboardTypeString];
} }
void _glfwPlatformSetPrimarySelectionString(const char* string) {
(void)string;
// Apple doesnt have a primary selection
}
const char* _glfwPlatformGetPrimarySelectionString(void) { return ""; }
const char* _glfwPlatformGetClipboardString(void) const char* _glfwPlatformGetClipboardString(void)
{ {
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
free(_glfw.ns.clipboardString); _glfw.ns.clipboardString = NULL;
if (![[pasteboard types] containsObject:NSPasteboardTypeString]) NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES};
{ NSArray* objs = [pasteboard readObjectsForClasses:@[[NSURL class], [NSString class]] options:options];
_glfwInputError(GLFW_FORMAT_UNAVAILABLE, if (objs) {
"Cocoa: Failed to retrieve string from pasteboard"); const NSUInteger count = [objs count];
return NULL; if (count) {
NSMutableString *path_list = [NSMutableString stringWithCapacity:4096]; // auto-released
NSMutableString *text_list = [NSMutableString stringWithCapacity:4096]; // auto-released
for (NSUInteger i = 0; i < count; i++) {
id obj = objs[i];
if ([obj isKindOfClass:[NSURL class]]) {
NSURL *url = (NSURL*)obj;
if (url.fileURL && url.fileSystemRepresentation) {
if ([path_list length] > 0) [path_list appendString:@("\n")];
[path_list appendString:@(url.fileSystemRepresentation)];
}
} else if ([obj isKindOfClass:[NSString class]]) {
if ([text_list length] > 0) [text_list appendString:@("\n")];
[text_list appendString:obj];
}
}
const char *text = NULL;
if (path_list.length > 0) text = [path_list UTF8String];
else if (text_list.length > 0) text = [text_list UTF8String];
if (text) _glfw.ns.clipboardString = _glfw_strdup(text);
}
} }
if (!_glfw.ns.clipboardString) _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to retrieve object from pasteboard");
NSString* object = [pasteboard stringForType:NSPasteboardTypeString];
if (!object)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Cocoa: Failed to retrieve object from pasteboard");
return NULL;
}
free(_glfw.ns.clipboardString);
_glfw.ns.clipboardString = _glfw_strdup([object UTF8String]);
return _glfw.ns.clipboardString; return _glfw.ns.clipboardString;
} }

9
glfw/context.c vendored
View File

@@ -396,15 +396,6 @@ bool _glfwRefreshContextAttribs(_GLFWwindow* window,
window->context.release = GLFW_RELEASE_BEHAVIOR_FLUSH; window->context.release = GLFW_RELEASE_BEHAVIOR_FLUSH;
} }
// Clearing the front buffer to black to avoid garbage pixels left over from
// previous uses of our bit of VRAM
{
PFNGLCLEARPROC glClear = (PFNGLCLEARPROC)
window->context.getProcAddress("glClear");
glClear(GL_COLOR_BUFFER_BIT);
window->context.swapBuffers(window);
}
glfwMakeContextCurrent((GLFWwindow*) previous); glfwMakeContextCurrent((GLFWwindow*) previous);
return true; return true;
} }

4
glfw/egl_context.c vendored
View File

@@ -428,6 +428,8 @@ bool _glfwInitEGL(void)
extensionSupportedEGL("EGL_KHR_get_all_proc_addresses"); extensionSupportedEGL("EGL_KHR_get_all_proc_addresses");
_glfw.egl.KHR_context_flush_control = _glfw.egl.KHR_context_flush_control =
extensionSupportedEGL("EGL_KHR_context_flush_control"); extensionSupportedEGL("EGL_KHR_context_flush_control");
_glfw.egl.EXT_present_opaque =
extensionSupportedEGL("EGL_EXT_present_opaque");
return true; return true;
} }
@@ -598,6 +600,8 @@ bool _glfwCreateContextEGL(_GLFWwindow* window,
if (_glfw.egl.KHR_gl_colorspace) if (_glfw.egl.KHR_gl_colorspace)
setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR);
} }
if (_glfw.egl.EXT_present_opaque)
setAttrib(EGL_PRESENT_OPAQUE_EXT, !fbconfig->transparent);
setAttrib(EGL_NONE, EGL_NONE); setAttrib(EGL_NONE, EGL_NONE);

9
glfw/egl_context.h vendored
View File

@@ -160,11 +160,11 @@ typedef EGLSurface (EGLAPIENTRY * PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)(EGLD
// //
typedef struct _GLFWcontextEGL typedef struct _GLFWcontextEGL
{ {
EGLConfig config; EGLConfig config;
EGLContext handle; EGLContext handle;
EGLSurface surface; EGLSurface surface;
void* client; void* client;
} _GLFWcontextEGL; } _GLFWcontextEGL;
@@ -186,6 +186,7 @@ typedef struct _GLFWlibraryEGL
bool EXT_platform_base; bool EXT_platform_base;
bool EXT_platform_x11; bool EXT_platform_x11;
bool EXT_platform_wayland; bool EXT_platform_wayland;
bool EXT_present_opaque;
bool ANGLE_platform_angle; bool ANGLE_platform_angle;
bool ANGLE_platform_angle_opengl; bool ANGLE_platform_angle_opengl;
bool ANGLE_platform_angle_d3d; bool ANGLE_platform_angle_d3d;

17
glfw/glfw3.h vendored
View File

@@ -512,6 +512,7 @@ typedef enum {
* GLFW_LOCK_KEY_MODS input mode is set. * GLFW_LOCK_KEY_MODS input mode is set.
*/ */
#define GLFW_MOD_NUM_LOCK 0x0080 #define GLFW_MOD_NUM_LOCK 0x0080
#define GLFW_MOD_LAST GLFW_MOD_NUM_LOCK
#define GLFW_LOCK_MASK (GLFW_MOD_NUM_LOCK | GLFW_MOD_CAPS_LOCK) #define GLFW_LOCK_MASK (GLFW_MOD_NUM_LOCK | GLFW_MOD_CAPS_LOCK)
/*! @} */ /*! @} */
@@ -1024,6 +1025,16 @@ typedef enum GLFWMouseButton {
* [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint). * [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint).
*/ */
#define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003 #define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003
/*! @brief macOS specific
* [window hint](@ref GLFW_COCOA_COLOR_SPACE_hint).
*/
#define GLFW_COCOA_COLOR_SPACE 0x00023004
typedef enum {
DEFAULT_COLORSPACE = 0,
SRGB_COLORSPACE = 1,
DISPLAY_P3_COLORSPACE = 2,
} GlfwCocoaColorSpaces;
/*! @brief X11 specific /*! @brief X11 specific
* [window hint](@ref GLFW_X11_CLASS_NAME_hint). * [window hint](@ref GLFW_X11_CLASS_NAME_hint).
*/ */
@@ -1206,7 +1217,8 @@ typedef enum {
typedef enum { typedef enum {
GLFW_IME_NONE, GLFW_IME_NONE,
GLFW_IME_PREEDIT_CHANGED, GLFW_IME_PREEDIT_CHANGED,
GLFW_IME_COMMIT_TEXT GLFW_IME_COMMIT_TEXT,
GLFW_IME_WAYLAND_DONE_EVENT,
} GLFWIMEState; } GLFWIMEState;
typedef enum { typedef enum {
@@ -1705,6 +1717,7 @@ typedef void (* GLFWjoystickfun)(int,int);
typedef void (* GLFWuserdatafun)(unsigned long long, void*); typedef void (* GLFWuserdatafun)(unsigned long long, void*);
typedef void (* GLFWtickcallback)(void*); typedef void (* GLFWtickcallback)(void*);
typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin); typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin);
typedef char* (* GLFWcurrentselectionfun)(void);
/*! @brief Video mode type. /*! @brief Video mode type.
* *
@@ -1862,6 +1875,7 @@ GLFWAPI unsigned long long glfwAddTimer(monotonic_t interval, bool repeats, GLFW
GLFWAPI void glfwUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled); GLFWAPI void glfwUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled);
GLFWAPI void glfwRemoveTimer(unsigned long long); GLFWAPI void glfwRemoveTimer(unsigned long long);
GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function); GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function);
GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun callback);
/*! @brief Terminates the GLFW library. /*! @brief Terminates the GLFW library.
* *
@@ -2757,6 +2771,7 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value);
GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share); GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share);
GLFWAPI bool glfwToggleFullscreen(GLFWwindow *window, unsigned int flags); GLFWAPI bool glfwToggleFullscreen(GLFWwindow *window, unsigned int flags);
GLFWAPI bool glfwIsFullscreen(GLFWwindow *window, unsigned int flags); GLFWAPI bool glfwIsFullscreen(GLFWwindow *window, unsigned int flags);
GLFWAPI bool glfwAreSwapsAllowed(const GLFWwindow* window);
/*! @brief Destroys the specified window and its context. /*! @brief Destroys the specified window and its context.
* *

7
glfw/init.c vendored
View File

@@ -386,3 +386,10 @@ GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun cbfun)
_GLFW_SWAP_POINTERS(_glfw.callbacks.draw_text, cbfun); _GLFW_SWAP_POINTERS(_glfw.callbacks.draw_text, cbfun);
return cbfun; return cbfun;
} }
GLFWAPI GLFWcurrentselectionfun glfwSetCurrentSelectionCallback(GLFWcurrentselectionfun cbfun)
{
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
_GLFW_SWAP_POINTERS(_glfw.callbacks.get_current_selection, cbfun);
return cbfun;
}

2
glfw/input.c vendored
View File

@@ -1540,7 +1540,7 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle UNUSED)
return _glfwPlatformGetClipboardString(); return _glfwPlatformGetClipboardString();
} }
#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) #if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) || defined(__APPLE__)
GLFWAPI void glfwSetPrimarySelectionString(GLFWwindow* handle UNUSED, const char* string) GLFWAPI void glfwSetPrimarySelectionString(GLFWwindow* handle UNUSED, const char* string)
{ {
assert(string != NULL); assert(string != NULL);

11
glfw/internal.h vendored
View File

@@ -52,6 +52,8 @@
#define GLFW_INCLUDE_NONE #define GLFW_INCLUDE_NONE
#include "glfw3.h" #include "glfw3.h"
#define EGL_PRESENT_OPAQUE_EXT 0x31df
#define _GLFW_INSERT_FIRST 0 #define _GLFW_INSERT_FIRST 0
#define _GLFW_INSERT_LAST 1 #define _GLFW_INSERT_LAST 1
@@ -307,6 +309,7 @@ struct _GLFWwndconfig
bool scaleToMonitor; bool scaleToMonitor;
struct { struct {
bool retina; bool retina;
int color_space;
char frameName[256]; char frameName[256];
} ns; } ns;
struct { struct {
@@ -438,6 +441,11 @@ struct _GLFWwindow
bool rawMouseMotion; bool rawMouseMotion;
_GLFWcontext context; _GLFWcontext context;
#ifdef _GLFW_WAYLAND
bool swaps_disallowed;
#else
const bool swaps_disallowed;
#endif
struct { struct {
GLFWwindowposfun pos; GLFWwindowposfun pos;
@@ -613,6 +621,7 @@ struct _GLFWlibrary
GLFWjoystickfun joystick; GLFWjoystickfun joystick;
GLFWapplicationclosefun application_close; GLFWapplicationclosefun application_close;
GLFWdrawtextfun draw_text; GLFWdrawtextfun draw_text;
GLFWcurrentselectionfun get_current_selection;
} callbacks; } callbacks;
@@ -667,7 +676,7 @@ void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
void _glfwPlatformSetClipboardString(const char* string); void _glfwPlatformSetClipboardString(const char* string);
const char* _glfwPlatformGetClipboardString(void); const char* _glfwPlatformGetClipboardString(void);
#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) #if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) || defined(__APPLE__)
void _glfwPlatformSetPrimarySelectionString(const char* string); void _glfwPlatformSetPrimarySelectionString(const char* string);
const char* _glfwPlatformGetPrimarySelectionString(void); const char* _glfwPlatformGetPrimarySelectionString(void);
#endif #endif

View File

@@ -10,77 +10,118 @@
#include <strings.h> #include <strings.h>
#include <string.h> #include <string.h>
static const char *DESKTOP_SERVICE = "org.freedesktop.portal.Desktop"; #define DESKTOP_SERVICE "org.freedesktop.portal.Desktop"
static const char *DESKTOP_PATH = "/org/freedesktop/portal/desktop"; #define DESKTOP_PATH "/org/freedesktop/portal/desktop"
static const char *DESKTOP_INTERFACE = "org.freedesktop.portal.Settings"; #define DESKTOP_INTERFACE "org.freedesktop.portal.Settings"
static const char *GNOME_DESKTOP_NAMESPACE = "org.gnome.desktop.interface"; #define GNOME_DESKTOP_NAMESPACE "org.gnome.desktop.interface"
#define FDO_DESKTOP_NAMESPACE "org.freedesktop.appearance"
#define FDO_APPEARANCE_KEY "color-scheme"
static char theme_name[64] = {0}; static char theme_name[128] = {0};
static int theme_size = -1; static int theme_size = -1;
static bool gnome_cursor_theme_read = false, gnome_cursor_size_read = false; static uint32_t appearance = 0;
static bool is_gnome = false;
static bool cursor_theme_changed = false;
static bool #define HANDLER(name) static void name(DBusMessage *msg, const char* errmsg, void *data) { \
parse_dbus_message_for_type(DBusMessage *const reply, const char *errmsg, const int type, void *value) {
DBusMessageIter iter[3];
dbus_message_iter_init(reply, &iter[0]);
#define FAIL { _glfwInputError(GLFW_PLATFORM_ERROR, "%s", errmsg); return false; }
if (dbus_message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_VARIANT) FAIL;
dbus_message_iter_recurse(&iter[0], &iter[1]);
if (dbus_message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_VARIANT) FAIL;
dbus_message_iter_recurse(&iter[1], &iter[2]);
if (dbus_message_iter_get_arg_type(&iter[2]) != type) FAIL;
dbus_message_iter_get_basic(&iter[2], value);
return true;
#undef FAIL
}
#define HANDLER(name) void name(DBusMessage *msg, const char* errmsg, void *data) { \
(void)data; \ (void)data; \
if (errmsg) { \ if (errmsg) { \
_glfwInputError(GLFW_PLATFORM_ERROR, "%s: failed with error: %s", #name, errmsg); \ _glfwInputError(GLFW_PLATFORM_ERROR, "%s: failed with error: %s", #name, errmsg); \
return; \ return; \
} }
HANDLER(on_gnome_cursor_theme_read) static void
const char *name; process_fdo_setting(const char *key, DBusMessageIter *value) {
if (!parse_dbus_message_for_type(msg, "Failed to get cursor theme name from reply", DBUS_TYPE_STRING, &name)) return; if (strcmp(key, FDO_APPEARANCE_KEY) == 0) {
if (name && name[0]) { if (dbus_message_iter_get_arg_type(value) == DBUS_TYPE_UINT32) {
gnome_cursor_theme_read = true; dbus_message_iter_get_basic(value, &appearance);
strncpy(theme_name, name, sizeof(theme_name) - 1); if (appearance > 2) appearance = 0;
if (gnome_cursor_size_read) _glfwPlatformChangeCursorTheme(); }
} }
} }
HANDLER(on_gnome_cursor_size_read)
int32_t sz;
if (!parse_dbus_message_for_type(msg, "Failed to get cursor theme size from reply", DBUS_TYPE_INT32, &sz)) return;
gnome_cursor_size_read = true;
theme_size = sz;
if (gnome_cursor_theme_read) _glfwPlatformChangeCursorTheme();
}
#undef HANDLER
static bool
call_read(DBusConnection *session_bus, dbus_pending_callback callback, const char *namespace, const char *key) {
return glfw_dbus_call_method_with_reply(
session_bus, DESKTOP_SERVICE, DESKTOP_PATH, DESKTOP_INTERFACE, "Read", DBUS_TIMEOUT_USE_DEFAULT,
callback, NULL, DBUS_TYPE_STRING, &namespace, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID);
}
static void static void
get_from_gnome(void) { process_gnome_setting(const char *key, DBusMessageIter *value) {
theme_size = 32; if (strcmp(key, "cursor-size") == 0) {
DBusConnection *session_bus = glfw_dbus_session_bus(); if (dbus_message_iter_get_arg_type(value) == DBUS_TYPE_INT32) {
if (session_bus) { int32_t sz;
const char *theme_key = "cursor-theme"; dbus_message_iter_get_basic(value, &sz);
call_read(session_bus, on_gnome_cursor_theme_read, GNOME_DESKTOP_NAMESPACE, theme_key); if (sz > 0 && sz != theme_size) {
const char *size_key = "cursor-size"; theme_size = sz;
call_read(session_bus, on_gnome_cursor_size_read, GNOME_DESKTOP_NAMESPACE, size_key); cursor_theme_changed = true;
}
}
} else if (strcmp(key, "cursor-theme") == 0) {
if (dbus_message_iter_get_arg_type(value) == DBUS_TYPE_STRING) {
const char *name;
dbus_message_iter_get_basic(value, &name);
if (name) {
strncpy(theme_name, name, sizeof(theme_name) - 1);
cursor_theme_changed = true;
}
}
} }
} }
static void
process_settings_dict(DBusMessageIter *array_iter, void(process_setting)(const char *, DBusMessageIter*)) {
DBusMessageIter item_iter, value_iter;
while (dbus_message_iter_get_arg_type(array_iter) == DBUS_TYPE_DICT_ENTRY) {
dbus_message_iter_recurse(array_iter, &item_iter);
if (dbus_message_iter_get_arg_type(&item_iter) == DBUS_TYPE_STRING) {
const char *key;
dbus_message_iter_get_basic(&item_iter, &key);
if (dbus_message_iter_next(&item_iter) && dbus_message_iter_get_arg_type(&item_iter) == DBUS_TYPE_VARIANT) {
dbus_message_iter_recurse(&item_iter, &value_iter);
process_setting(key, &value_iter);
}
}
if (!dbus_message_iter_next(array_iter)) break;
}
}
HANDLER(process_desktop_settings)
cursor_theme_changed = false;
DBusMessageIter root, array, item, settings;
dbus_message_iter_init(msg, &root);
#define die(...) { _glfwInputError(GLFW_PLATFORM_ERROR, __VA_ARGS__); return; }
if (dbus_message_iter_get_arg_type(&root) != DBUS_TYPE_ARRAY) die("Reply to request for desktop settings is not an array");
dbus_message_iter_recurse(&root, &array);
while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_DICT_ENTRY) {
dbus_message_iter_recurse(&array, &item);
if (dbus_message_iter_get_arg_type(&item) == DBUS_TYPE_STRING) {
const char *namespace;
dbus_message_iter_get_basic(&item, &namespace);
if (dbus_message_iter_next(&item) && dbus_message_iter_get_arg_type(&item) == DBUS_TYPE_ARRAY) {
dbus_message_iter_recurse(&item, &settings);
if (strcmp(namespace, FDO_DESKTOP_NAMESPACE) == 0) {
process_settings_dict(&settings, process_fdo_setting);
} else if (is_gnome && strcmp(namespace, GNOME_DESKTOP_NAMESPACE) == 0) {
process_settings_dict(&settings, process_gnome_setting);
}
}
}
if (!dbus_message_iter_next(&array)) break;
}
#undef die
if (cursor_theme_changed) _glfwPlatformChangeCursorTheme();
}
#undef HANDLER
static bool
read_desktop_settings(DBusConnection *session_bus) {
DBusMessage *msg = dbus_message_new_method_call(DESKTOP_SERVICE, DESKTOP_PATH, DESKTOP_INTERFACE, "ReadAll");
if (!msg) return false;
DBusMessageIter iter, array_iter;
dbus_message_iter_init_append(msg, &iter);
if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &array_iter)) { dbus_message_unref(msg); return false; }
if (!dbus_message_iter_close_container(&iter, &array_iter)) { dbus_message_unref(msg); return false; }
bool ok = call_method_with_msg(session_bus, msg, DBUS_TIMEOUT_USE_DEFAULT, process_desktop_settings, NULL);
dbus_message_unref(msg);
return ok;
}
void void
glfw_current_cursor_theme(const char **theme, int *size) { glfw_current_cursor_theme(const char **theme, int *size) {
@@ -100,10 +141,55 @@ get_cursor_theme_from_env(void) {
} }
} }
static void
on_color_scheme_change(DBusMessage *message) {
DBusMessageIter iter[2];
dbus_message_iter_init (message, &iter[0]);
int current_type;
while ((current_type = dbus_message_iter_get_arg_type (&iter[0])) != DBUS_TYPE_INVALID) {
if (current_type == DBUS_TYPE_VARIANT) {
dbus_message_iter_recurse(&iter[0], &iter[1]);
if (dbus_message_iter_get_arg_type(&iter[1]) == DBUS_TYPE_UINT32) {
uint32_t val = 0;
dbus_message_iter_get_basic(&iter[1], &val);
if (val > 2) val = 0;
if (val != appearance) {
appearance = val;
}
}
break;
}
dbus_message_iter_next(&iter[0]);
}
}
static DBusHandlerResult
setting_changed(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data UNUSED) {
/* printf("session_bus settings_changed invoked interface: %s member: %s\n", dbus_message_get_interface(msg), dbus_message_get_member(msg)); */
if (dbus_message_is_signal(msg, DESKTOP_INTERFACE, "SettingChanged")) {
const char *namespace = NULL, *key = NULL;
if (glfw_dbus_get_args(msg, "Failed to get namespace and key from SettingChanged notification signal", DBUS_TYPE_STRING, &namespace, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) {
if (strcmp(namespace, FDO_DESKTOP_NAMESPACE) == 0) {
if (strcmp(key, FDO_APPEARANCE_KEY) == 0) {
on_color_scheme_change(msg);
}
}
}
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
void void
glfw_initialize_desktop_settings(void) { glfw_initialize_desktop_settings(void) {
get_cursor_theme_from_env(); get_cursor_theme_from_env();
const char *desktop = getenv("XDG_CURRENT_DESKTOP"); const char *desktop = getenv("XDG_CURRENT_DESKTOP");
bool is_gnome = desktop && strncasecmp(desktop, "GNOME", sizeof("GNOME") - 1) == 0; is_gnome = desktop && strstr(desktop, "GNOME");
if (is_gnome) get_from_gnome(); DBusConnection *session_bus = glfw_dbus_session_bus();
if (session_bus) {
if (!read_desktop_settings(session_bus)) _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to read desktop settings, make sure you have the desktop portal running.");
dbus_bus_add_match(session_bus, "type='signal',interface='" DESKTOP_INTERFACE "',member='SettingChanged'", NULL);
dbus_connection_add_filter(session_bus, setting_changed, NULL, NULL);
}
} }

9
glfw/window.c vendored
View File

@@ -332,6 +332,8 @@ void glfwDefaultWindowHints(void)
// The default is to use full Retina resolution framebuffers // The default is to use full Retina resolution framebuffers
_glfw.hints.window.ns.retina = true; _glfw.hints.window.ns.retina = true;
// use the default colorspace assigned by the system
_glfw.hints.window.ns.color_space = 0;
} }
GLFWAPI void glfwWindowHint(int hint, int value) GLFWAPI void glfwWindowHint(int hint, int value)
@@ -412,6 +414,9 @@ GLFWAPI void glfwWindowHint(int hint, int value)
case GLFW_COCOA_RETINA_FRAMEBUFFER: case GLFW_COCOA_RETINA_FRAMEBUFFER:
_glfw.hints.window.ns.retina = value ? true : false; _glfw.hints.window.ns.retina = value ? true : false;
return; return;
case GLFW_COCOA_COLOR_SPACE:
_glfw.hints.window.ns.color_space = value;
return;
case GLFW_COCOA_GRAPHICS_SWITCHING: case GLFW_COCOA_GRAPHICS_SWITCHING:
_glfw.hints.context.nsgl.offline = value ? true : false; _glfw.hints.context.nsgl.offline = value ? true : false;
return; return;
@@ -1062,6 +1067,10 @@ GLFWAPI bool glfwIsFullscreen(GLFWwindow* wh, unsigned int flags) {
return _glfwPlatformIsFullscreen((_GLFWwindow*)wh, flags); return _glfwPlatformIsFullscreen((_GLFWwindow*)wh, flags);
} }
GLFWAPI bool glfwAreSwapsAllowed(const GLFWwindow* wh) {
return !(((_GLFWwindow*)wh)->swaps_disallowed);
}
GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* handle, void* pointer) GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* handle, void* pointer)
{ {
_GLFWwindow* window = (_GLFWwindow*) handle; _GLFWwindow* window = (_GLFWwindow*) handle;

View File

@@ -31,13 +31,13 @@ static void
build_blur_kernel(kernel_type *blur_kernel, const size_t size, kernel_type sigma) { build_blur_kernel(kernel_type *blur_kernel, const size_t size, kernel_type sigma) {
// 1D Normalized Gaussian // 1D Normalized Gaussian
const kernel_type half = size / (kernel_type)2; const kernel_type half = size / (kernel_type)2;
kernel_type sum = 0; kernel_type sum = 0;
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
kernel_type f = (i - half); kernel_type f = (i - half);
blur_kernel[i] = (kernel_type)exp(- f * f / sigma); blur_kernel[i] = (kernel_type)exp(- f * f / sigma);
sum += blur_kernel[i]; sum += blur_kernel[i];
} }
for (size_t i = 0; i < size; i++) blur_kernel[i] /= sum; for (size_t i = 0; i < size; i++) blur_kernel[i] /= sum;
} }
static void static void
@@ -102,13 +102,31 @@ init_buffer_pair(_GLFWWaylandBufferPair *pair, size_t width, size_t height, unsi
return 2 * pair->size_in_bytes; return 2 * pair->size_in_bytes;
} }
static bool
window_has_buffer(_GLFWwindow *window, struct wl_buffer *q) {
#define Q(which) decs.which.buffer.a == q || decs.which.buffer.b == q
return Q(left) || Q(top) || Q(right) || Q(bottom);
#undef Q
}
static void static void
alloc_buffer_pair(_GLFWWaylandBufferPair *pair, struct wl_shm_pool *pool, uint8_t *data, size_t *offset) { buffer_release_event(void *data, struct wl_buffer *buffer) {
wl_buffer_destroy(buffer);
_GLFWwindow *window = _glfwWindowForId((uintptr_t)data);
if (window && window_has_buffer(window, buffer)) decs.buffer_destroyed = true;
}
static struct wl_buffer_listener handle_buffer_events = {.release = buffer_release_event};
static void
alloc_buffer_pair(uintptr_t window_id, _GLFWWaylandBufferPair *pair, struct wl_shm_pool *pool, uint8_t *data, size_t *offset) {
pair->data.a = data + *offset; pair->data.a = data + *offset;
pair->a = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888); pair->a = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
wl_buffer_add_listener(pair->a, &handle_buffer_events, (void*)window_id);
*offset += pair->size_in_bytes; *offset += pair->size_in_bytes;
pair->data.b = data + *offset; pair->data.b = data + *offset;
pair->b = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888); pair->b = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
wl_buffer_add_listener(pair->b, &handle_buffer_events, (void*)window_id);
*offset += pair->size_in_bytes; *offset += pair->size_in_bytes;
pair->front = pair->a; pair->back = pair->b; pair->front = pair->a; pair->back = pair->b;
pair->data.front = pair->data.a; pair->data.back = pair->data.b; pair->data.front = pair->data.a; pair->data.back = pair->data.b;
@@ -287,7 +305,7 @@ create_shm_buffers(_GLFWwindow* window) {
struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, decs.mapping.size); struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, decs.mapping.size);
close(fd); close(fd);
size_t offset = 0; size_t offset = 0;
#define a(which) alloc_buffer_pair(&decs.which.buffer, pool, decs.mapping.data, &offset) #define a(which) alloc_buffer_pair(window->id, &decs.which.buffer, pool, decs.mapping.data, &offset)
a(top); a(left); a(bottom); a(right); a(top); a(left); a(bottom); a(right);
#undef a #undef a
wl_shm_pool_destroy(pool); wl_shm_pool_destroy(pool);
@@ -313,8 +331,6 @@ free_csd_surfaces(_GLFWwindow *window) {
static void static void
free_csd_buffers(_GLFWwindow *window) { free_csd_buffers(_GLFWwindow *window) {
#define d(which) { \ #define d(which) { \
if (decs.which.buffer.a) wl_buffer_destroy(decs.which.buffer.a); \
if (decs.which.buffer.b) wl_buffer_destroy(decs.which.buffer.b); \
memset(&decs.which.buffer, 0, sizeof(_GLFWWaylandBufferPair)); \ memset(&decs.which.buffer, 0, sizeof(_GLFWWaylandBufferPair)); \
} }
d(left); d(top); d(right); d(bottom); d(left); d(top); d(right); d(bottom);
@@ -352,11 +368,14 @@ ensure_csd_resources(_GLFWwindow *window) {
decs.for_window_state.scale != window->wl.scale || decs.for_window_state.scale != window->wl.scale ||
!decs.mapping.data !decs.mapping.data
); );
const bool needs_update = focus_changed || size_changed || !decs.left.surface; const bool needs_update = focus_changed || size_changed || !decs.left.surface || decs.buffer_destroyed;
debug("CSD: old.size: %dx%d new.size: %dx%d needs_update: %d size_changed: %d buffer_destroyed: %d\n",
decs.for_window_state.width, decs.for_window_state.height, window->wl.width, window->wl.height, needs_update, size_changed, decs.buffer_destroyed);
if (!needs_update) return false; if (!needs_update) return false;
if (size_changed) { if (size_changed || decs.buffer_destroyed) {
free_csd_buffers(window); free_csd_buffers(window);
if (!create_shm_buffers(window)) return false; if (!create_shm_buffers(window)) return false;
decs.buffer_destroyed = false;
} }
int32_t x, y, scale = window->wl.scale < 1 ? 1 : window->wl.scale; int32_t x, y, scale = window->wl.scale < 1 ? 1 : window->wl.scale;
@@ -409,7 +428,7 @@ change_csd_title(_GLFWwindow *window) {
void void
set_csd_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height) { set_csd_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height) {
bool has_csd = window->decorated && !window->wl.decorations.serverSide && window->wl.decorations.left.surface && !(window->wl.toplevel_states & TOPLEVEL_STATE_FULLSCREEN); bool has_csd = window->decorated && !window->wl.decorations.serverSide && window->wl.decorations.left.surface && !(window->wl.current.toplevel_states & TOPLEVEL_STATE_FULLSCREEN);
bool size_specified_by_compositor = *width > 0 && *height > 0; bool size_specified_by_compositor = *width > 0 && *height > 0;
if (!size_specified_by_compositor) { if (!size_specified_by_compositor) {
*width = window->wl.user_requested_content_size.width; *width = window->wl.user_requested_content_size.width;

13
glfw/wl_init.c vendored
View File

@@ -54,6 +54,8 @@
#endif #endif
#define debug(...) if (_glfw.hints.init.debugRendering) fprintf(stderr, __VA_ARGS__);
static int min(int n1, int n2) static int min(int n1, int n2)
{ {
return n1 < n2 ? n1 : n2; return n1 < n2 ? n1 : n2;
@@ -96,7 +98,6 @@ static void pointerHandleEnter(void* data UNUSED,
if (!window) if (!window)
return; return;
} }
window->wl.decorations.focus = focus; window->wl.decorations.focus = focus;
_glfw.wl.serial = serial; _glfw.wl.serial = serial;
_glfw.wl.pointerFocus = window; _glfw.wl.pointerFocus = window;
@@ -142,10 +143,15 @@ static void setCursor(GLFWCursorShape shape, _GLFWwindow* window)
if (!image) if (!image)
return; return;
if (image->width % scale || image->height % scale) {
_glfwInputError(GLFW_PLATFORM_ERROR, "WARNING: Cursor image size: %dx%d is not a multiple of window scale: %d. This will"
" cause some compositors such as GNOME to crash. See https://github.com/kovidgoyal/kitty/issues/4878", image->width, image->height, scale);
}
buffer = wl_cursor_image_get_buffer(image); buffer = wl_cursor_image_get_buffer(image);
if (!buffer) if (!buffer)
return; return;
debug("Calling wl_pointer_set_cursor in setCursor with surface: %p\n", (void*)surface);
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
surface, surface,
image->hotspot_x / scale, image->hotspot_x / scale,
@@ -243,7 +249,7 @@ static void pointerHandleButton(void* data UNUSED,
window->wl.decorations.last_click_on_top_decoration_at = monotonic(); window->wl.decorations.last_click_on_top_decoration_at = monotonic();
if (window->wl.decorations.last_click_on_top_decoration_at - last_click_at <= _glfwPlatformGetDoubleClickInterval(window)) { if (window->wl.decorations.last_click_on_top_decoration_at - last_click_at <= _glfwPlatformGetDoubleClickInterval(window)) {
window->wl.decorations.last_click_on_top_decoration_at = 0; window->wl.decorations.last_click_on_top_decoration_at = 0;
if (window->wl.toplevel_states & TOPLEVEL_STATE_MAXIMIZED) if (window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED)
xdg_toplevel_unset_maximized(window->wl.xdg.toplevel); xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
else else
xdg_toplevel_set_maximized(window->wl.xdg.toplevel); xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
@@ -567,6 +573,7 @@ static void seatHandleCapabilities(void* data UNUSED,
{ {
wl_pointer_destroy(_glfw.wl.pointer); wl_pointer_destroy(_glfw.wl.pointer);
_glfw.wl.pointer = NULL; _glfw.wl.pointer = NULL;
if (_glfw.wl.cursorAnimationTimer) toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0);
} }
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard) if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard)
@@ -578,6 +585,8 @@ static void seatHandleCapabilities(void* data UNUSED,
{ {
wl_keyboard_destroy(_glfw.wl.keyboard); wl_keyboard_destroy(_glfw.wl.keyboard);
_glfw.wl.keyboard = NULL; _glfw.wl.keyboard = NULL;
_glfw.wl.keyboardFocusId = 0;
if (_glfw.wl.keyRepeatInfo.keyRepeatTimer) toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 0);
} }
} }

26
glfw/wl_platform.h vendored
View File

@@ -116,17 +116,21 @@ typedef enum WaylandWindowState {
TOPLEVEL_STATE_NONE = 0, TOPLEVEL_STATE_NONE = 0,
TOPLEVEL_STATE_MAXIMIZED = 1, TOPLEVEL_STATE_MAXIMIZED = 1,
TOPLEVEL_STATE_FULLSCREEN = 2, TOPLEVEL_STATE_FULLSCREEN = 2,
TOPLEVEL_STATE_RESIZING = 4, TOPLEVEL_STATE_RESIZING = 4,
TOPLEVEL_STATE_ACTIVATED = 8, TOPLEVEL_STATE_ACTIVATED = 8,
TOPLEVEL_STATE_TILED_LEFT = 16, TOPLEVEL_STATE_TILED_LEFT = 16,
TOPLEVEL_STATE_TILED_RIGHT = 32, TOPLEVEL_STATE_TILED_RIGHT = 32,
TOPLEVEL_STATE_TILED_TOP = 64, TOPLEVEL_STATE_TILED_TOP = 64,
TOPLEVEL_STATE_TILED_BOTTOM = 128, TOPLEVEL_STATE_TILED_BOTTOM = 128,
} WaylandWindowState; } WaylandWindowState;
static const WaylandWindowState TOPLEVEL_STATE_DOCKED = TOPLEVEL_STATE_MAXIMIZED | TOPLEVEL_STATE_FULLSCREEN | TOPLEVEL_STATE_TILED_TOP | TOPLEVEL_STATE_TILED_LEFT | TOPLEVEL_STATE_TILED_RIGHT | TOPLEVEL_STATE_TILED_BOTTOM; static const WaylandWindowState TOPLEVEL_STATE_DOCKED = TOPLEVEL_STATE_MAXIMIZED | TOPLEVEL_STATE_FULLSCREEN | TOPLEVEL_STATE_TILED_TOP | TOPLEVEL_STATE_TILED_LEFT | TOPLEVEL_STATE_TILED_RIGHT | TOPLEVEL_STATE_TILED_BOTTOM;
enum WaylandWindowPendingState {
PENDING_STATE_TOPLEVEL = 1,
PENDING_STATE_DECORATION = 2
};
// Wayland-specific per-window data // Wayland-specific per-window data
// //
@@ -168,7 +172,7 @@ typedef struct _GLFWwindowWayland
struct zwp_idle_inhibitor_v1* idleInhibitor; struct zwp_idle_inhibitor_v1* idleInhibitor;
struct { struct {
bool serverSide; bool serverSide, buffer_destroyed;
_GLFWdecorationSideWayland focus; _GLFWdecorationSideWayland focus;
_GLFWWaylandCSDEdge top, left, right, bottom; _GLFWWaylandCSDEdge top, left, right, bottom;
@@ -210,14 +214,20 @@ typedef struct _GLFWwindowWayland
int32_t width, height; int32_t width, height;
} user_requested_content_size; } user_requested_content_size;
uint32_t toplevel_states;
bool maximize_on_first_show; bool maximize_on_first_show;
// counters for ignoring axis events following axis_discrete events in the // counters for ignoring axis events following axis_discrete events in the
// same frame along the same axis // same frame along the same axis
struct { struct {
unsigned int x, y; unsigned int x, y;
} axis_discrete_count; } axis_discrete_count;
bool surface_configured_once;
uint32_t pending_state;
struct {
int width, height;
uint32_t toplevel_states;
uint32_t decoration_mode;
} current, pending;
} _GLFWwindowWayland; } _GLFWwindowWayland;
typedef enum _GLFWWaylandOfferType typedef enum _GLFWWaylandOfferType

24
glfw/wl_text_input.c vendored
View File

@@ -47,7 +47,7 @@ static void
send_text(const char *text, GLFWIMEState ime_state) { send_text(const char *text, GLFWIMEState ime_state) {
_GLFWwindow *w = _glfwFocusedWindow(); _GLFWwindow *w = _glfwFocusedWindow();
if (w && w->callbacks.keyboard) { if (w && w->callbacks.keyboard) {
GLFWkeyevent fake_ev = {.action = GLFW_PRESS}; GLFWkeyevent fake_ev = {.action = text ? GLFW_PRESS : GLFW_RELEASE};
fake_ev.text = text; fake_ev.text = text;
fake_ev.ime_state = ime_state; fake_ev.ime_state = ime_state;
w->callbacks.keyboard((GLFWwindow*) w, &fake_ev); w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);
@@ -84,15 +84,18 @@ text_input_delete_surrounding_text(
} }
static void static void
text_input_done(void *data UNUSED, struct zwp_text_input_v3 *txt_input UNUSED, uint32_t serial UNUSED) { text_input_done(void *data UNUSED, struct zwp_text_input_v3 *txt_input UNUSED, uint32_t serial) {
debug("text-input: done event: serial: %u current_commit_serial: %u\n", serial, commit_serial); debug("text-input: done event: serial: %u current_commit_serial: %u\n", serial, commit_serial);
if (serial != commit_serial) { if (serial != commit_serial) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: text_input_done serial mismatch, expected=%u got=%u\n", commit_serial, serial); if (serial > commit_serial) _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: text_input_done serial mismatch, expected=%u got=%u\n", commit_serial, serial);
return; return;
} }
if (pending_pre_edit) { if (pending_pre_edit) {
send_text(pending_pre_edit, GLFW_IME_PREEDIT_CHANGED); send_text(pending_pre_edit, GLFW_IME_PREEDIT_CHANGED);
free(pending_pre_edit); pending_pre_edit = NULL; free(pending_pre_edit); pending_pre_edit = NULL;
} else {
// Clear pre-edit text
send_text(NULL, GLFW_IME_WAYLAND_DONE_EVENT);
} }
if (pending_commit) { if (pending_commit) {
send_text(pending_commit, GLFW_IME_COMMIT_TEXT); send_text(pending_commit, GLFW_IME_COMMIT_TEXT);
@@ -139,7 +142,20 @@ _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
switch(ev->type) { switch(ev->type) {
case GLFW_IME_UPDATE_FOCUS: case GLFW_IME_UPDATE_FOCUS:
debug("\ntext-input: updating IME focus state, focused: %d\n", ev->focused); debug("\ntext-input: updating IME focus state, focused: %d\n", ev->focused);
if (ev->focused) zwp_text_input_v3_enable(text_input); else zwp_text_input_v3_disable(text_input); if (ev->focused) {
zwp_text_input_v3_enable(text_input);
zwp_text_input_v3_set_content_type(text_input, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL);
} else {
if (pending_pre_edit) {
// Clear pre-edit text
send_text(NULL, GLFW_IME_PREEDIT_CHANGED);
free(pending_pre_edit); pending_pre_edit = NULL;
}
if (pending_commit) {
free(pending_commit); pending_commit = NULL;
}
zwp_text_input_v3_disable(text_input);
}
commit(); commit();
break; break;
case GLFW_IME_UPDATE_CURSOR_POSITION: { case GLFW_IME_UPDATE_CURSOR_POSITION: {

121
glfw/wl_window.c vendored
View File

@@ -147,6 +147,7 @@ setCursorImage(_GLFWwindow* window, bool on_theme_change) {
cursorWayland->yhot = image->hotspot_y; cursorWayland->yhot = image->hotspot_y;
} }
debug("Calling wl_pointer_set_cursor in setCursorImage with surface: %p\n", (void*)surface);
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
surface, surface,
cursorWayland->xhot / scale, cursorWayland->xhot / scale,
@@ -234,7 +235,7 @@ clipboard_mime(void) {
return buf; return buf;
} }
static void static bool
dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height) { dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height) {
bool size_changed = width != window->wl.width || height != window->wl.height; bool size_changed = width != window->wl.width || height != window->wl.height;
bool scale_changed = checkScaleChange(window); bool scale_changed = checkScaleChange(window);
@@ -246,12 +247,14 @@ dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height
} }
if (scale_changed) { if (scale_changed) {
if (!size_changed) debug("Scale changed to %d in dispatchChangesAfterConfigure\n", window->wl.scale);
resizeFramebuffer(window); if (!size_changed) resizeFramebuffer(window);
_glfwInputWindowContentScale(window, window->wl.scale, window->wl.scale); _glfwInputWindowContentScale(window, window->wl.scale, window->wl.scale);
} }
_glfwInputWindowDamage(window); _glfwInputWindowDamage(window);
return size_changed || scale_changed;
} }
static void static void
@@ -269,24 +272,9 @@ xdgDecorationHandleConfigure(void* data,
uint32_t mode) uint32_t mode)
{ {
_GLFWwindow* window = data; _GLFWwindow* window = data;
window->wl.pending.decoration_mode = mode;
bool has_server_side_decorations = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); window->wl.pending_state |= PENDING_STATE_DECORATION;
debug("XDG decoration configure event received: has_server_side_decorations: %d\n", has_server_side_decorations); debug("XDG decoration configure event received: has_server_side_decorations: %d\n", (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE));
if (has_server_side_decorations == window->wl.decorations.serverSide) return;
window->wl.decorations.serverSide = has_server_side_decorations;
int width = window->wl.width, height = window->wl.height;
if (window->wl.decorations.serverSide) {
free_csd_surfaces(window);
height += window->wl.decorations.metrics.visible_titlebar_height;
} else {
ensure_csd_resources(window);
}
set_csd_window_geometry(window, &width, &height);
dispatchChangesAfterConfigure(window, width, height);
ensure_csd_resources(window);
wl_surface_commit(window->wl.surface);
debug("final window content size: %dx%d\n", window->wl.width, window->wl.height);
inform_compositor_of_window_geometry(window, "configure-decorations");
} }
static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = { static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = {
@@ -311,6 +299,7 @@ static void surfaceHandleEnter(void *data,
window->wl.monitors[window->wl.monitorsCount++] = monitor; window->wl.monitors[window->wl.monitorsCount++] = monitor;
if (checkScaleChange(window)) { if (checkScaleChange(window)) {
debug("Scale changed to %d in surface enter event\n", window->wl.scale);
resizeFramebuffer(window); resizeFramebuffer(window);
_glfwInputWindowContentScale(window, window->wl.scale, window->wl.scale); _glfwInputWindowContentScale(window, window->wl.scale, window->wl.scale);
ensure_csd_resources(window); ensure_csd_resources(window);
@@ -336,6 +325,7 @@ static void surfaceHandleLeave(void *data,
window->wl.monitors[--window->wl.monitorsCount] = NULL; window->wl.monitors[--window->wl.monitorsCount] = NULL;
if (checkScaleChange(window)) { if (checkScaleChange(window)) {
debug("Scale changed to %d in surface leave event\n", window->wl.scale);
resizeFramebuffer(window); resizeFramebuffer(window);
_glfwInputWindowContentScale(window, window->wl.scale, window->wl.scale); _glfwInputWindowContentScale(window, window->wl.scale, window->wl.scale);
ensure_csd_resources(window); ensure_csd_resources(window);
@@ -416,7 +406,7 @@ static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, bool on)
bool bool
_glfwPlatformIsFullscreen(_GLFWwindow *window, unsigned int flags UNUSED) { _glfwPlatformIsFullscreen(_GLFWwindow *window, unsigned int flags UNUSED) {
return window->wl.toplevel_states & TOPLEVEL_STATE_FULLSCREEN; return window->wl.current.toplevel_states & TOPLEVEL_STATE_FULLSCREEN;
} }
bool bool
@@ -458,7 +448,7 @@ xdgToplevelHandleConfigure(void* data,
if (new_states & TOPLEVEL_STATE_RESIZING) { if (new_states & TOPLEVEL_STATE_RESIZING) {
if (width) window->wl.user_requested_content_size.width = width; if (width) window->wl.user_requested_content_size.width = width;
if (height) window->wl.user_requested_content_size.height = height; if (height) window->wl.user_requested_content_size.height = height;
if (!(window->wl.toplevel_states & TOPLEVEL_STATE_RESIZING)) _glfwInputLiveResize(window, true); if (!(window->wl.current.toplevel_states & TOPLEVEL_STATE_RESIZING)) _glfwInputLiveResize(window, true);
} }
if (width != 0 && height != 0) if (width != 0 && height != 0)
{ {
@@ -475,16 +465,11 @@ xdgToplevelHandleConfigure(void* data,
} }
} }
} }
bool live_resize_done = !(new_states & TOPLEVEL_STATE_RESIZING) && (window->wl.toplevel_states & TOPLEVEL_STATE_RESIZING);
window->wl.toplevel_states = new_states; window->wl.pending.toplevel_states = new_states;
set_csd_window_geometry(window, &width, &height); window->wl.pending.width = width;
dispatchChangesAfterConfigure(window, width, height); window->wl.pending.height = height;
debug("final window content size: %dx%d\n", window->wl.width, window->wl.height); window->wl.pending_state |= PENDING_STATE_TOPLEVEL;
_glfwInputWindowFocus(window, window->wl.toplevel_states & TOPLEVEL_STATE_ACTIVATED);
ensure_csd_resources(window);
wl_surface_commit(window->wl.surface);
inform_compositor_of_window_geometry(window, "configure");
if (live_resize_done) _glfwInputLiveResize(window, false);
} }
static void xdgToplevelHandleClose(void* data, static void xdgToplevelHandleClose(void* data,
@@ -499,11 +484,68 @@ static const struct xdg_toplevel_listener xdgToplevelListener = {
xdgToplevelHandleClose xdgToplevelHandleClose
}; };
static void xdgSurfaceHandleConfigure(void* data UNUSED, static void xdgSurfaceHandleConfigure(void* data,
struct xdg_surface* surface, struct xdg_surface* surface,
uint32_t serial) uint32_t serial)
{ {
_GLFWwindow* window = data;
xdg_surface_ack_configure(surface, serial); xdg_surface_ack_configure(surface, serial);
if (window->wl.pending_state & PENDING_STATE_TOPLEVEL) {
uint32_t new_states = window->wl.pending.toplevel_states;
int width = window->wl.pending.width;
int height = window->wl.pending.height;
if (!window->wl.surface_configured_once) {
window->wl.surface_configured_once = true;
// this will attach the buffer to the surface, the client is responsible for clearing the buffer to an appropriate blank
window->swaps_disallowed = false;
window->context.swapBuffers(window);
if (!width && !height && !new_states && !window->wl.decorations.serverSide && getenv("XAUTHORITY") && strstr(getenv("XAUTHORITY"), "mutter")) {
// https://github.com/kovidgoyal/kitty/issues/4802
debug("Ignoring first empty surface configure event on mutter.\n");
return;
}
}
if (new_states != window->wl.current.toplevel_states ||
width != window->wl.current.width ||
height != window->wl.current.height) {
bool live_resize_done = !(new_states & TOPLEVEL_STATE_RESIZING) && (window->wl.current.toplevel_states & TOPLEVEL_STATE_RESIZING);
window->wl.current.toplevel_states = new_states;
window->wl.current.width = width;
window->wl.current.height = height;
_glfwInputWindowFocus(window, window->wl.current.toplevel_states & TOPLEVEL_STATE_ACTIVATED);
if (live_resize_done) _glfwInputLiveResize(window, false);
}
}
if (window->wl.pending_state & PENDING_STATE_DECORATION) {
uint32_t mode = window->wl.pending.decoration_mode;
bool has_server_side_decorations = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
window->wl.decorations.serverSide = has_server_side_decorations;
window->wl.current.decoration_mode = mode;
}
bool resized = false;
if (window->wl.pending_state) {
int width = window->wl.pending.width, height = window->wl.pending.height;
set_csd_window_geometry(window, &width, &height);
resized = dispatchChangesAfterConfigure(window, width, height);
if (window->wl.decorations.serverSide) {
free_csd_surfaces(window);
} else {
ensure_csd_resources(window);
}
debug("final window content size: %dx%d resized: %d\n", width, height, resized);
}
inform_compositor_of_window_geometry(window, "configure");
// if a resize happened there will be a commit at the next render frame so
// dont commit here, GNOME doesnt like it and its not really needed anyway
if (!resized) wl_surface_commit(window->wl.surface);
window->wl.pending_state = 0;
} }
static const struct xdg_surface_listener xdgSurfaceListener = { static const struct xdg_surface_listener xdgSurfaceListener = {
@@ -746,6 +788,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
initialize_csd_metrics(window); initialize_csd_metrics(window);
window->wl.transparent = fbconfig->transparent; window->wl.transparent = fbconfig->transparent;
strncpy(window->wl.appId, wndconfig->wl.appId, sizeof(window->wl.appId)); strncpy(window->wl.appId, wndconfig->wl.appId, sizeof(window->wl.appId));
window->swaps_disallowed = true;
if (!createSurface(window, wndconfig)) if (!createSurface(window, wndconfig))
return false; return false;
@@ -955,7 +998,7 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
if (window->decorated && !window->monitor && !window->wl.decorations.serverSide) if (window->decorated && !window->monitor && !window->wl.decorations.serverSide)
{ {
if (top) if (top)
*top = window->wl.decorations.metrics.top; *top = window->wl.decorations.metrics.top - window->wl.decorations.metrics.visible_titlebar_height;
if (left) if (left)
*left = window->wl.decorations.metrics.width; *left = window->wl.decorations.metrics.width;
if (right) if (right)
@@ -991,7 +1034,7 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window)
{ {
if (window->monitor) if (window->monitor)
xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
if (window->wl.toplevel_states & TOPLEVEL_STATE_MAXIMIZED) if (window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED)
xdg_toplevel_unset_maximized(window->wl.xdg.toplevel); xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
// There is no way to unset minimized, or even to know if we are // There is no way to unset minimized, or even to know if we are
// minimized, so there is nothing to do in this case. // minimized, so there is nothing to do in this case.
@@ -1024,6 +1067,8 @@ void _glfwPlatformHideWindow(_GLFWwindow* window)
xdg_surface_destroy(window->wl.xdg.surface); xdg_surface_destroy(window->wl.xdg.surface);
window->wl.xdg.toplevel = NULL; window->wl.xdg.toplevel = NULL;
window->wl.xdg.surface = NULL; window->wl.xdg.surface = NULL;
window->wl.surface_configured_once = false;
window->swaps_disallowed = true;
} }
window->wl.visible = false; window->wl.visible = false;
} }
@@ -1092,7 +1137,7 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window)
int _glfwPlatformWindowMaximized(_GLFWwindow* window) int _glfwPlatformWindowMaximized(_GLFWwindow* window)
{ {
return window->wl.toplevel_states & TOPLEVEL_STATE_MAXIMIZED; return window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED;
} }
int _glfwPlatformWindowHovered(_GLFWwindow* window) int _glfwPlatformWindowHovered(_GLFWwindow* window)
@@ -1360,6 +1405,7 @@ static void lockPointer(_GLFWwindow* window)
window->wl.pointerLock.relativePointer = relativePointer; window->wl.pointerLock.relativePointer = relativePointer;
window->wl.pointerLock.lockedPointer = lockedPointer; window->wl.pointerLock.lockedPointer = lockedPointer;
debug("Calling wl_pointer_set_cursor in lockPointer with surface: %p\n", NULL);
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
NULL, 0, 0); NULL, 0, 0);
} }
@@ -1396,6 +1442,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
} }
else if (window->cursorMode == GLFW_CURSOR_HIDDEN) else if (window->cursorMode == GLFW_CURSOR_HIDDEN)
{ {
debug("Calling wl_pointer_set_cursor in _glfwPlatformSetCursor with surface: %p\n", NULL);
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0); wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial, NULL, 0, 0);
} }
} }

View File

@@ -2,10 +2,11 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import os import os
import re
import sys import sys
from contextlib import suppress from contextlib import suppress
from typing import ( from typing import (
TYPE_CHECKING, Callable, Dict, List, NamedTuple, Optional, Tuple TYPE_CHECKING, Callable, Dict, Iterator, List, NamedTuple, Optional, Tuple
) )
from kitty.cli import parse_args from kitty.cli import parse_args
@@ -16,11 +17,12 @@ from kitty.typing import BossType, KeyEventType, TypedDict
from kitty.utils import ScreenSize from kitty.utils import ScreenSize
from ..tui.handler import Handler, result_handler from ..tui.handler import Handler, result_handler
from ..tui.loop import Loop, MouseEvent from ..tui.loop import Loop, MouseEvent, debug
from ..tui.operations import MouseTracking, alternate_screen, styled from ..tui.operations import MouseTracking, alternate_screen, styled
if TYPE_CHECKING: if TYPE_CHECKING:
import readline import readline
debug
else: else:
readline = None readline = None
@@ -74,7 +76,7 @@ class HistoryCompleter:
def option_text() -> str: def option_text() -> str:
return '''\ return '''\
--type -t --type -t
choices=line,yesno,choices choices=line,yesno,choices,password
default=line default=line
Type of input. Defaults to asking for a line of text. Type of input. Defaults to asking for a line of text.
@@ -92,17 +94,31 @@ be used for completions and via the browse history readline bindings.
--choice -c --choice -c
type=list type=list
dest=choices dest=choices
A choice for the choices type. Every choice has the syntax: letter:text Where A choice for the choices type. Can be specified multiple times. Every choice has
letter is the accelerator key and text is the corresponding text. There can be the syntax: ``letter[;color]:text``. Where :italic:`letter` is the accelerator key
an optional color specification after the letter to indicate what color it should and :italic:`text` is the corresponding text. There can be an optional color
be. specification after the letter to indicate what color it should be.
For example: y:Yes and n;red:No For example: :code:`y:Yes` and :code:`n;red:No`
--default -d --default -d
A default choice or text. If unspecified, it is "y" for :code:`yesno`, the first choice A default choice or text. If unspecified, it is :code:`y` for the type
for :code:`choices` and empty for others. The default choice is selected when the user :code:`yesno`, the first choice for :code:`choices` and empty for others types.
presses the Enter key. The default choice is selected when the user presses the :kbd:`Enter` key.
--prompt -p
default="> "
The prompt to use when inputting a line of text or a password.
--unhide-key
default=u
The key to be pressed to unhide hidden text
--hidden-text-placeholder
The text in the message to be replaced by hidden text. The hidden text is read via STDIN.
''' '''
@@ -140,10 +156,63 @@ def extra_for(width: int, screen_width: int) -> int:
return max(0, screen_width - width) // 2 + 1 return max(0, screen_width - width) // 2 + 1
class Choose(Handler): class Password(Handler):
def __init__(self, cli_opts: AskCLIOptions, prompt: str, is_password: bool = True, initial_text: str = '') -> None:
self.cli_opts = cli_opts
self.prompt = prompt
self.initial_text = initial_text
from kittens.tui.line_edit import LineEdit
self.line_edit = LineEdit(is_password=is_password)
def initialize(self) -> None:
self.cmd.set_cursor_shape('beam')
if self.initial_text:
self.line_edit.on_text(self.initial_text, True)
self.draw_screen()
@Handler.atomic_update
def draw_screen(self) -> None:
self.cmd.clear_screen()
if self.cli_opts.message:
for line in self.cli_opts.message.splitlines():
self.print(line)
self.print()
self.line_edit.write(self.write, self.prompt)
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
self.line_edit.on_text(text, in_bracketed_paste)
self.draw_screen()
def on_key(self, key_event: KeyEventType) -> None:
if self.line_edit.on_key(key_event):
self.draw_screen()
return
if key_event.matches('enter'):
self.quit_loop(0)
if key_event.matches('esc'):
self.quit_loop(1)
def on_resize(self, screen_size: ScreenSize) -> None:
self.screen_size = screen_size
self.draw_screen()
def on_interrupt(self) -> None:
self.quit_loop(1)
on_eot = on_interrupt
@property
def response(self) -> str:
if self._tui_loop.return_code == 0:
return self.line_edit.current_input
return ''
class Choose(Handler): # {{{
mouse_tracking = MouseTracking.buttons_only mouse_tracking = MouseTracking.buttons_only
def __init__(self, cli_opts: AskCLIOptions) -> None: def __init__(self, cli_opts: AskCLIOptions) -> None:
self.prefix_style_pat = re.compile(r'(?:\x1b\[[^m]*?m)+')
self.cli_opts = cli_opts self.cli_opts = cli_opts
self.choices: Dict[str, Choice] = {} self.choices: Dict[str, Choice] = {}
self.clickable_ranges: Dict[str, List[Range]] = {} self.clickable_ranges: Dict[str, List[Range]] = {}
@@ -165,6 +234,18 @@ class Choose(Handler):
self.response_on_accept = cli_opts.default or '' self.response_on_accept = cli_opts.default or ''
if cli_opts.type in ('yesno', 'choices') and self.response_on_accept not in self.allowed: if cli_opts.type in ('yesno', 'choices') and self.response_on_accept not in self.allowed:
self.response_on_accept = 'y' if cli_opts.type == 'yesno' else tuple(self.choices.keys())[0] self.response_on_accept = 'y' if cli_opts.type == 'yesno' else tuple(self.choices.keys())[0]
self.message = cli_opts.message
self.hidden_text_start_pos = self.hidden_text_end_pos = -1
self.hidden_text = ''
self.replacement_text = t = f'Press {styled(self.cli_opts.unhide_key, fg="green")} or click to show'
self.replacement_range = Range(-1, -1, -1)
if self.message and self.cli_opts.hidden_text_placeholder:
self.hidden_text_start_pos = self.message.find(self.cli_opts.hidden_text_placeholder)
if self.hidden_text_start_pos > -1:
self.hidden_text = sys.stdin.read().rstrip()
self.hidden_text_end_pos = self.hidden_text_start_pos + len(t)
suffix = self.message[self.hidden_text_start_pos + len(self.cli_opts.hidden_text_placeholder):]
self.message = self.message[:self.hidden_text_start_pos] + t + suffix
def initialize(self) -> None: def initialize(self) -> None:
self.cmd.set_cursor_visible(False) self.cmd.set_cursor_visible(False)
@@ -173,24 +254,35 @@ class Choose(Handler):
def finalize(self) -> None: def finalize(self) -> None:
self.cmd.set_cursor_visible(True) self.cmd.set_cursor_visible(True)
def draw_long_text(self, text: str) -> int: def draw_long_text(self, text: str) -> Iterator[str]:
y = 0 if not text:
yield ''
return
width = self.screen_size.cols - 2 width = self.screen_size.cols - 2
m = self.prefix_style_pat.match(text)
prefix = m.group() if m else ''
while text: while text:
t, text = truncate_at_space(text, width) t, text = truncate_at_space(text, width)
t = t.strip() t = t.strip()
self.print(' ' * extra_for(wcswidth(t), width), styled(t, bold=True), sep='') yield ' ' * extra_for(wcswidth(t), width) + styled(prefix + t, bold=True)
y += 1
return y
@Handler.atomic_update @Handler.atomic_update
def draw_screen(self) -> None: def draw_screen(self) -> None:
self.cmd.clear_screen() self.cmd.clear_screen()
y = max(0, self.screen_size.rows // 2 - 2) msg_lines: List[str] = []
if self.message:
for line in self.message.splitlines():
msg_lines.extend(self.draw_long_text(line))
y = self.screen_size.rows - len(msg_lines)
y = max(0, (y // 2) - 2)
self.print(end='\r\n'*y) self.print(end='\r\n'*y)
if self.cli_opts.message: for line in msg_lines:
for line in self.cli_opts.message.splitlines(): if self.replacement_text in line:
y += self.draw_long_text(line) idx = line.find(self.replacement_text)
x = wcswidth(line[:idx])
self.replacement_range = Range(x, x + wcswidth(self.replacement_text), y)
self.print(line)
y += 1
if self.screen_size.rows > 2: if self.screen_size.rows > 2:
self.print() self.print()
y += 1 y += 1
@@ -205,7 +297,8 @@ class Choose(Handler):
current_line_length = 0 current_line_length = 0
current_line: List[Tuple[str, str]] = [] current_line: List[Tuple[str, str]] = []
lines: List[List[Tuple[str, str]]] = [] lines: List[List[Tuple[str, str]]] = []
sep, sep_sz = ' ', 2 sep = ' '
sep_sz = len(sep) + 2 # for the borders
for choice in choices: for choice in choices:
self.clickable_ranges[choice.letter] = [] self.clickable_ranges[choice.letter] = []
@@ -218,7 +311,7 @@ class Choose(Handler):
current_line = [] current_line = []
current_line_length = 0 current_line_length = 0
current_line.append((choice.letter, text)) current_line.append((choice.letter, text))
current_line_length += sz current_line_length += sz + sep_sz
if current_line: if current_line:
lines.append(current_line) lines.append(current_line)
@@ -257,10 +350,12 @@ class Choose(Handler):
self.print(' ' * offset, line, sep='', end='' if is_last else '\r\n') self.print(' ' * offset, line, sep='', end='' if is_last else '\r\n')
y += 1 y += 1
self.cmd.set_line_wrapping(False)
for boxed_line in lines: for boxed_line in lines:
print_line(top, *boxed_line) print_line(top, *boxed_line)
print_line(middle, *boxed_line) print_line(middle, *boxed_line)
print_line(bottom, *boxed_line, is_last=boxed_line is lines[-1]) print_line(bottom, *boxed_line, is_last=boxed_line is lines[-1])
self.cmd.set_line_wrapping(True)
def draw_choice(self, y: int) -> None: def draw_choice(self, y: int) -> None:
if y + 3 <= self.screen_size.rows: if y + 3 <= self.screen_size.rows:
@@ -316,6 +411,14 @@ class Choose(Handler):
self.quit_loop(0) self.quit_loop(0)
elif self.cli_opts.type == 'yesno': elif self.cli_opts.type == 'yesno':
self.on_interrupt() self.on_interrupt()
elif self.hidden_text and text == self.cli_opts.unhide_key:
self.unhide()
def unhide(self) -> None:
if self.hidden_text and self.message:
self.message = self.message[:self.hidden_text_start_pos] + self.hidden_text + self.message[self.hidden_text_end_pos:]
self.hidden_text = ''
self.draw_screen()
def on_key(self, key_event: KeyEventType) -> None: def on_key(self, key_event: KeyEventType) -> None:
if key_event.matches('esc'): if key_event.matches('esc'):
@@ -331,6 +434,8 @@ class Choose(Handler):
self.response = letter self.response = letter
self.quit_loop(0) self.quit_loop(0)
return return
if self.hidden_text and self.replacement_range.has_point(ev.cell_x, ev.cell_y):
self.unhide()
def on_resize(self, screen_size: ScreenSize) -> None: def on_resize(self, screen_size: ScreenSize) -> None:
self.screen_size = screen_size self.screen_size = screen_size
@@ -339,6 +444,7 @@ class Choose(Handler):
def on_interrupt(self) -> None: def on_interrupt(self) -> None:
self.quit_loop(1) self.quit_loop(1)
on_eot = on_interrupt on_eot = on_interrupt
# }}}
def main(args: List[str]) -> Response: def main(args: List[str]) -> Response:
@@ -352,7 +458,7 @@ def main(args: List[str]) -> Response:
except SystemExit as e: except SystemExit as e:
if e.code != 0: if e.code != 0:
print(e.args[0]) print(e.args[0])
input('Press enter to quit...') input('Press Enter to quit')
raise SystemExit(e.code) raise SystemExit(e.code)
if cli_opts.type in ('yesno', 'choices'): if cli_opts.type in ('yesno', 'choices'):
@@ -361,6 +467,15 @@ def main(args: List[str]) -> Response:
loop.loop(handler) loop.loop(handler)
return {'items': items, 'response': handler.response} return {'items': items, 'response': handler.response}
prompt = cli_opts.prompt
if prompt[0] == prompt[-1] and prompt[0] in '\'"':
prompt = prompt[1:-1]
if cli_opts.type == 'password':
loop = Loop()
phandler = Password(cli_opts, prompt)
loop.loop(phandler)
return {'items': items, 'response': phandler.response}
import readline as rl import readline as rl
readline = rl readline = rl
from kitty.shell import init_readline from kitty.shell import init_readline
@@ -371,7 +486,6 @@ def main(args: List[str]) -> Response:
if cli_opts.message: if cli_opts.message:
print(styled(cli_opts.message, bold=True)) print(styled(cli_opts.message, bold=True))
prompt = '> '
with suppress(KeyboardInterrupt, EOFError): with suppress(KeyboardInterrupt, EOFError):
if cli_opts.default: if cli_opts.default:
def prefill_text() -> None: def prefill_text() -> None:

View File

@@ -99,7 +99,7 @@ class Broadcast(Handler):
OPTIONS = (MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')).format OPTIONS = (MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')).format
help_text = 'Broadcast typed text to all kitty windows. By default text is sent to all windows, unless one of the matching options is specified' help_text = 'Broadcast typed text to kitty windows. By default text is sent to all windows, unless one of the matching options is specified'
usage = '[initial text to send ...]' usage = '[initial text to send ...]'

View File

@@ -231,11 +231,11 @@ static PyMethodDef module_methods[] = {
}; };
static struct PyModuleDef module = { static struct PyModuleDef module = {
.m_base = PyModuleDef_HEAD_INIT, .m_base = PyModuleDef_HEAD_INIT,
.m_name = "subseq_matcher", /* name of module */ .m_name = "subseq_matcher", /* name of module */
.m_doc = NULL, .m_doc = NULL,
.m_size = -1, .m_size = -1,
.m_methods = module_methods .m_methods = module_methods
}; };
EXPORTED PyMODINIT_FUNC EXPORTED PyMODINIT_FUNC

View File

@@ -1,63 +1,27 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import codecs
import io
import os import os
import select
import sys import sys
from typing import List, NoReturn, Optional from typing import List, NoReturn, Optional
from kitty.cli import parse_args from kitty.cli import parse_args
from kitty.cli_stub import ClipboardCLIOptions from kitty.cli_stub import ClipboardCLIOptions
from kitty.fast_data_types import parse_input_from_terminal
from ..tui.handler import Handler from ..tui.operations import (
from ..tui.loop import Loop raw_mode, request_from_clipboard, write_to_clipboard
)
class Clipboard(Handler):
def __init__(self, data_to_send: Optional[bytes], args: ClipboardCLIOptions):
self.args = args
self.clipboard_contents: Optional[str] = None
self.data_to_send = data_to_send
self.quit_on_write = False
def initialize(self) -> None:
if self.data_to_send is not None:
self.cmd.write_to_clipboard(self.data_to_send, self.args.use_primary)
if not self.args.get_clipboard:
if self.args.wait_for_completion:
# ask kitty for the TN terminfo capability and
# only quit after a response is received
self.print('\x1bP+q544e\x1b\\', end='')
self.print('Waiting for completion...')
return
self.quit_on_write = True
return
self.cmd.request_from_clipboard(self.args.use_primary)
def on_writing_finished(self) -> None:
if self.quit_on_write:
self.quit_loop(0)
def on_clipboard_response(self, text: str, from_primary: bool = False) -> None:
self.clipboard_contents = text
self.quit_loop(0)
def on_capability_response(self, name: str, val: str) -> None:
self.quit_loop(0)
def on_interrupt(self) -> None:
self.quit_loop(1)
def on_eot(self) -> None:
self.quit_loop(1)
OPTIONS = r''' OPTIONS = r'''
--get-clipboard --get-clipboard
default=False default=False
type=bool-set type=bool-set
Output the current contents of the clipboard to STDOUT. Note that by default Output the current contents of the clipboard to STDOUT. Note that by default
kitty will prompt you asking to allow access to the clipboard. Can be controlled kitty will prompt for permission to access the clipboard. Can be controlled
by :opt:`clipboard_control`. by :opt:`clipboard_control`.
@@ -84,6 +48,62 @@ popup, see :opt:`clipboard_control` for details.
''' '''
usage = '' usage = ''
got_capability_response = False
got_clipboard_response = False
clipboard_contents = ''
clipboard_from_primary = False
def ignore(x: str) -> None:
pass
def on_text(x: str) -> None:
if '\x03' in x:
raise KeyboardInterrupt()
if '\x04' in x:
raise EOFError()
def on_dcs(dcs: str) -> None:
global got_capability_response
if dcs.startswith('1+r'):
got_capability_response = True
def on_osc(osc: str) -> None:
global clipboard_contents, clipboard_from_primary, got_clipboard_response
idx = osc.find(';')
if idx <= 0:
return
q = osc[:idx]
if q == '52':
got_clipboard_response = True
widx = osc.find(';', idx + 1)
if widx < idx:
clipboard_from_primary = osc.find('p', idx + 1) > -1
clipboard_contents = ''
else:
from base64 import standard_b64decode
clipboard_from_primary = osc.find('p', idx+1, widx) > -1
data = memoryview(osc.encode('ascii'))
clipboard_contents = standard_b64decode(data[widx+1:]).decode('utf-8')
def wait_loop(tty_fd: int) -> None:
os.set_blocking(tty_fd, False)
decoder = codecs.getincrementaldecoder('utf-8')('ignore')
with raw_mode(tty_fd):
buf = ''
while not got_capability_response and not got_clipboard_response:
rd = select.select([tty_fd], [], [])[0]
if rd:
raw = os.read(tty_fd, io.DEFAULT_BUFFER_SIZE)
if not raw:
raise EOFError()
data = decoder.decode(raw)
buf = (buf + data) if buf else data
buf = parse_input_from_terminal(on_text, on_dcs, ignore, on_osc, ignore, ignore, buf, False)
def main(args: List[str]) -> NoReturn: def main(args: List[str]) -> NoReturn:
@@ -93,14 +113,34 @@ def main(args: List[str]) -> NoReturn:
data: Optional[bytes] = None data: Optional[bytes] = None
if not sys.stdin.isatty(): if not sys.stdin.isatty():
data = sys.stdin.buffer.read() data = sys.stdin.buffer.read()
sys.stdin = open(os.ctermid()) wait_for_capability_response = False
loop = Loop() data_to_write = []
handler = Clipboard(data, cli_opts) if data:
loop.loop(handler) data_to_write.append(write_to_clipboard(data, cli_opts.use_primary).encode('ascii'))
if loop.return_code == 0 and handler.clipboard_contents: if not cli_opts.get_clipboard and cli_opts.wait_for_completion:
sys.stdout.write(handler.clipboard_contents) data_to_write.append(b'\x1bP+q544e\x1b\\')
sys.stdout.flush() wait_for_capability_response = True
raise SystemExit(loop.return_code) if cli_opts.get_clipboard:
data_to_write.append(request_from_clipboard(cli_opts.use_primary).encode('ascii'))
wait_for_capability_response = True
tty_fd = os.open(os.ctermid(), os.O_RDWR | os.O_CLOEXEC)
retcode = 0
with open(tty_fd, 'wb', closefd=True) as ttyf:
for x in data_to_write:
ttyf.write(x)
ttyf.flush()
if wait_for_capability_response:
try:
wait_loop(tty_fd)
except KeyboardInterrupt:
sys.excepthook = lambda *a: None
raise
except EOFError:
retcode = 1
if clipboard_contents:
print(end=clipboard_contents)
raise SystemExit(retcode)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -4,10 +4,14 @@
import os import os
import re import re
from contextlib import suppress from contextlib import suppress
from fnmatch import fnmatch
from functools import lru_cache from functools import lru_cache
from hashlib import md5 from hashlib import md5
from typing import (
TYPE_CHECKING, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Union
)
from kitty.guess_mime_type import guess_type from kitty.guess_mime_type import guess_type
from typing import TYPE_CHECKING, Dict, List, Set, Optional, Iterator, Tuple, Union
if TYPE_CHECKING: if TYPE_CHECKING:
from .highlight import DiffHighlight # noqa from .highlight import DiffHighlight # noqa
@@ -37,6 +41,8 @@ class Segment:
class Collection: class Collection:
ignore_names: Tuple[str, ...] = ()
def __init__(self) -> None: def __init__(self) -> None:
self.changes: Dict[str, str] = {} self.changes: Dict[str, str] = {}
self.renames: Dict[str, str] = {} self.renames: Dict[str, str] = {}
@@ -105,22 +111,34 @@ def resolve_remote_name(path: str, default: str) -> str:
return default return default
def allowed_items(items: Sequence[str], ignore_patterns: Sequence[str]) -> Iterator[str]:
for name in items:
for pat in ignore_patterns:
if fnmatch(name, pat):
break
else:
yield name
def walk(base: str, names: Set[str], pmap: Dict[str, str], ignore_names: Tuple[str, ...]) -> None:
for dirpath, dirnames, filenames in os.walk(base):
dirnames[:] = allowed_items(dirnames, ignore_names)
for filename in allowed_items(filenames, ignore_names):
path = os.path.abspath(os.path.join(dirpath, filename))
path_name_map[path] = name = os.path.relpath(path, base)
names.add(name)
pmap[name] = path
def collect_files(collection: Collection, left: str, right: str) -> None: def collect_files(collection: Collection, left: str, right: str) -> None:
left_names: Set[str] = set() left_names: Set[str] = set()
right_names: Set[str] = set() right_names: Set[str] = set()
left_path_map: Dict[str, str] = {} left_path_map: Dict[str, str] = {}
right_path_map: Dict[str, str] = {} right_path_map: Dict[str, str] = {}
def walk(base: str, names: Set[str], pmap: Dict[str, str]) -> None: walk(left, left_names, left_path_map, collection.ignore_names)
for dirpath, dirnames, filenames in os.walk(base): walk(right, right_names, right_path_map, collection.ignore_names)
for filename in filenames:
path = os.path.abspath(os.path.join(dirpath, filename))
path_name_map[path] = name = os.path.relpath(path, base)
names.add(name)
pmap[name] = path
walk(left, left_names, left_path_map)
walk(right, right_names, right_path_map)
common_names = left_names & right_names common_names = left_names & right_names
changed_names = {n for n in common_names if data_for_path(left_path_map[n]) != data_for_path(right_path_map[n])} changed_names = {n for n in common_names if data_for_path(left_path_map[n]) != data_for_path(right_path_map[n])}
for n in changed_names: for n in changed_names:

View File

@@ -161,13 +161,15 @@ def highlight_collection(collection: Collection, aliases: Optional[Dict[str, str
if p: if p:
is_binary = isinstance(data_for_path(p), bytes) is_binary = isinstance(data_for_path(p), bytes)
if not is_binary: if not is_binary:
jobs[executor.submit(highlight_for_diff, p, aliases)] = p jobs[executor.submit(highlight_for_diff, p, aliases or {})] = p
for future in concurrent.futures.as_completed(jobs): for future in concurrent.futures.as_completed(jobs):
path = jobs[future] path = jobs[future]
try: try:
highlights = future.result() highlights = future.result()
except Exception as e: except Exception as e:
return f'Running syntax highlighting for {path} generated an exception: {e}' import traceback
tb = traceback.format_exc()
return f'Running syntax highlighting for {path} generated an exception: {e} with traceback:\n{tb}'
ans[path] = highlights ans[path] = highlights
return ans return ans

View File

@@ -293,10 +293,11 @@ class DiffHandler(Handler):
def scroll_lines(self, amt: int = 1) -> None: def scroll_lines(self, amt: int = 1) -> None:
new_pos = max(0, min(self.scroll_pos + amt, self.max_scroll_pos)) new_pos = max(0, min(self.scroll_pos + amt, self.max_scroll_pos))
amt = new_pos - self.scroll_pos
if new_pos == self.scroll_pos: if new_pos == self.scroll_pos:
self.cmd.bell() self.cmd.bell()
return return
if abs(new_pos - self.scroll_pos) >= self.num_lines - 1: if abs(amt) >= self.num_lines - 1:
self.scroll_pos = new_pos self.scroll_pos = new_pos
self.draw_screen() self.draw_screen()
return return
@@ -570,8 +571,8 @@ OPTIONS = partial('''\
--context --context
type=int type=int
default=-1 default=-1
Number of lines of context to show between changes. Negative values Number of lines of context to show between changes. Negative values use the
use the number set in diff.conf number set in :file:`diff.conf`.
--config --config
@@ -598,7 +599,7 @@ class ShowWarning:
showwarning = ShowWarning() showwarning = ShowWarning()
help_text = 'Show a side-by-side diff of the specified files/directories. You can also use ssh:hostname:remote-file-path to diff remote files.' help_text = 'Show a side-by-side diff of the specified files/directories. You can also use :italic:`ssh:hostname:remote-file-path` to diff remote files.'
usage = 'file_or_directory_left file_or_directory_right' usage = 'file_or_directory_left file_or_directory_right'
@@ -654,6 +655,7 @@ def main(args: List[str]) -> None:
opts = init_config(cli_opts) opts = init_config(cli_opts)
set_diff_command(opts.diff_cmd) set_diff_command(opts.diff_cmd)
lines_for_path.replace_tab_by = opts.replace_tab_by lines_for_path.replace_tab_by = opts.replace_tab_by
Collection.ignore_names = tuple(opts.ignore_name)
left, right = map(get_remote_file, (left, right)) left, right = map(get_remote_file, (left, right))
if os.path.isdir(left) != os.path.isdir(right): if os.path.isdir(left) != os.path.isdir(right):
raise SystemExit('The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.') raise SystemExit('The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.')

View File

@@ -24,8 +24,8 @@ agr('diff', 'Diffing')
opt('syntax_aliases', 'pyj:py pyi:py recipe:py', opt('syntax_aliases', 'pyj:py pyi:py recipe:py',
option_type='syntax_aliases', option_type='syntax_aliases',
long_text=''' long_text='''
File extension aliases for syntax highlight For example, to syntax highlight File extension aliases for syntax highlight. For example, to syntax highlight
:file:`file.xyz` as :file:`file.abc` use a setting of :code:`xyz:abc` :file:`file.xyz` as :file:`file.abc` use a setting of :code:`xyz:abc`.
''' '''
) )
@@ -37,8 +37,9 @@ opt('num_context_lines', '3',
opt('diff_cmd', 'auto', opt('diff_cmd', 'auto',
long_text=''' long_text='''
The diff command to use. Must contain the placeholder :code:`_CONTEXT_` which The diff command to use. Must contain the placeholder :code:`_CONTEXT_` which
will be replaced by the number of lines of context. The default is to search the will be replaced by the number of lines of context. The default special value
system for either git or diff and use that, if found. :code:`auto` is to search the system for either :program:`git` or
:program:`diff` and use that, if found.
''' '''
) )
@@ -46,6 +47,21 @@ opt('replace_tab_by', '\\x20\\x20\\x20\\x20',
option_type='python_string', option_type='python_string',
long_text='The string to replace tabs with. Default is to use four spaces.' long_text='The string to replace tabs with. Default is to use four spaces.'
) )
opt('+ignore_name', '',
option_type='store_multiple',
add_to_default=False,
long_text='''
A glob pattern that is matched against only the filename of files and directories. Matching
files and directories are ignored when scanning the filesystem to look for files to diff.
Can be specified multiple times to use multiple patterns. For example::
ignore_name .git
ignore_name *~
ignore_name *.pyc
''',
)
egr() # }}} egr() # }}}
# colors {{{ # colors {{{

View File

@@ -1,7 +1,7 @@
# generated by gen-config.py DO NOT edit # generated by gen-config.py DO NOT edit
import typing import typing
from kittens.diff.options.utils import parse_map, syntax_aliases from kittens.diff.options.utils import parse_map, store_multiple, syntax_aliases
from kitty.conf.utils import merge_dicts, positive_int, python_string, to_color, to_color_or_none from kitty.conf.utils import merge_dicts, positive_int, python_string, to_color, to_color_or_none
@@ -37,6 +37,10 @@ class Parser:
def hunk_margin_bg(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: def hunk_margin_bg(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['hunk_margin_bg'] = to_color(val) ans['hunk_margin_bg'] = to_color(val)
def ignore_name(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
for k, v in store_multiple(val, ans["ignore_name"]):
ans["ignore_name"][k] = v
def margin_bg(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: def margin_bg(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
ans['margin_bg'] = to_color(val) ans['margin_bg'] = to_color(val)
@@ -89,11 +93,12 @@ class Parser:
def create_result_dict() -> typing.Dict[str, typing.Any]: def create_result_dict() -> typing.Dict[str, typing.Any]:
return { return {
'ignore_name': {},
'map': [], 'map': [],
} }
actions = frozenset(('map',)) actions: typing.FrozenSet[str] = frozenset(('map',))
def merge_result_dicts(defaults: typing.Dict[str, typing.Any], vals: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]: def merge_result_dicts(defaults: typing.Dict[str, typing.Any], vals: typing.Dict[str, typing.Any]) -> typing.Dict[str, typing.Any]:

View File

@@ -20,6 +20,7 @@ option_names = ( # {{{
'highlight_removed_bg', 'highlight_removed_bg',
'hunk_bg', 'hunk_bg',
'hunk_margin_bg', 'hunk_margin_bg',
'ignore_name',
'map', 'map',
'margin_bg', 'margin_bg',
'margin_fg', 'margin_fg',
@@ -64,6 +65,7 @@ class Options:
syntax_aliases: typing.Dict[str, str] = {'pyj': 'py', 'pyi': 'py', 'recipe': 'py'} syntax_aliases: typing.Dict[str, str] = {'pyj': 'py', 'pyi': 'py', 'recipe': 'py'}
title_bg: Color = Color(255, 255, 255) title_bg: Color = Color(255, 255, 255)
title_fg: Color = Color(0, 0, 0) title_fg: Color = Color(0, 0, 0)
ignore_name: typing.Dict[str, str] = {}
map: typing.List[typing.Tuple[kitty.types.ParsedShortcut, kitty.conf.utils.KeyAction]] = [] map: typing.List[typing.Tuple[kitty.types.ParsedShortcut, kitty.conf.utils.KeyAction]] = []
key_definitions: KittensKeyMap = {} key_definitions: KittensKeyMap = {}
config_paths: typing.Tuple[str, ...] = () config_paths: typing.Tuple[str, ...] = ()
@@ -116,6 +118,7 @@ class Options:
defaults = Options() defaults = Options()
defaults.ignore_name = {}
defaults.map = [ defaults.map = [
# quit # quit
(ParsedShortcut(mods=0, key_name='q'), KeyAction('quit')), # noqa (ParsedShortcut(mods=0, key_name='q'), KeyAction('quit')), # noqa

View File

@@ -3,7 +3,7 @@
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net> # License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
from typing import Any, Dict, Iterable, Tuple, Union from typing import Any, Container, Dict, Iterable, Tuple, Union
from kitty.conf.utils import ( from kitty.conf.utils import (
KeyFuncWrapper, KittensKeyDefinition, parse_kittens_key KeyFuncWrapper, KittensKeyDefinition, parse_kittens_key
@@ -58,6 +58,12 @@ def syntax_aliases(raw: str) -> Dict[str, str]:
return ans return ans
def store_multiple(val: str, current_val: Container[str]) -> Iterable[Tuple[str, str]]:
val = val.strip()
if val not in current_val:
yield val, val
def parse_map(val: str) -> Iterable[KittensKeyDefinition]: def parse_map(val: str) -> Iterable[KittensKeyDefinition]:
x = parse_kittens_key(val, func_with_args.args_funcs) x = parse_kittens_key(val, func_with_args.args_funcs)
if x is not None: if x is not None:

View File

@@ -153,7 +153,7 @@ class Hunk:
if c.left_start + c.left_count != self.left_start + self.left_count: if c.left_start + c.left_count != self.left_start + self.left_count:
raise ValueError(f'Left side line mismatch {c.left_start + c.left_count} != {self.left_start + self.left_count}') raise ValueError(f'Left side line mismatch {c.left_start + c.left_count} != {self.left_start + self.left_count}')
if c.right_start + c.right_count != self.right_start + self.right_count: if c.right_start + c.right_count != self.right_start + self.right_count:
raise ValueError(f'Left side line mismatch {c.right_start + c.right_count} != {self.right_start + self.right_count}') raise ValueError(f'Right side line mismatch {c.right_start + c.right_count} != {self.right_start + self.right_count}')
for c in self.chunks: for c in self.chunks:
c.finalize() c.finalize()
@@ -200,7 +200,7 @@ def parse_patch(raw: str) -> Patch:
else: else:
if current_hunk is None: if current_hunk is None:
continue continue
q = line[0] q = line[0] if line else ''
if q == '+': if q == '+':
current_hunk.add_line() current_hunk.add_line()
elif q == '-': elif q == '-':

View File

@@ -36,6 +36,8 @@ def images_supported() -> bool:
class Ref: class Ref:
__slots__: Tuple[str, ...] = ()
def __setattr__(self, name: str, value: object) -> None: def __setattr__(self, name: str, value: object) -> None:
raise AttributeError("can't set attribute") raise AttributeError("can't set attribute")

View File

@@ -187,11 +187,11 @@ static PyMethodDef module_methods[] = {
}; };
static struct PyModuleDef module = { static struct PyModuleDef module = {
.m_base = PyModuleDef_HEAD_INIT, .m_base = PyModuleDef_HEAD_INIT,
.m_name = "diff_speedup", /* name of module */ .m_name = "diff_speedup", /* name of module */
.m_doc = NULL, .m_doc = NULL,
.m_size = -1, .m_size = -1,
.m_methods = module_methods .m_methods = module_methods
}; };
EXPORTED PyMODINIT_FUNC EXPORTED PyMODINIT_FUNC

View File

@@ -9,14 +9,14 @@ from functools import lru_cache
from gettext import gettext as _ from gettext import gettext as _
from itertools import repeat from itertools import repeat
from typing import ( from typing import (
Any, Callable, Dict, Generator, Iterable, List, Optional, Pattern, Any, Callable, Dict, Generator, Iterable, Iterator, List, Optional,
Sequence, Set, Tuple, Type, cast Pattern, Sequence, Set, Tuple, Type, cast
) )
from kitty.cli import parse_args from kitty.cli import parse_args
from kitty.cli_stub import HintsCLIOptions from kitty.cli_stub import HintsCLIOptions
from kitty.constants import website_url from kitty.constants import website_url
from kitty.fast_data_types import get_options, set_clipboard_string from kitty.fast_data_types import get_options, set_clipboard_string, wcswidth
from kitty.key_encoding import KeyEvent from kitty.key_encoding import KeyEvent
from kitty.typing import BossType, KittyCommonOpts from kitty.typing import BossType, KittyCommonOpts
from kitty.utils import ( from kitty.utils import (
@@ -27,6 +27,7 @@ from kitty.utils import (
from ..tui.handler import Handler, result_handler from ..tui.handler import Handler, result_handler
from ..tui.loop import Loop from ..tui.loop import Loop
from ..tui.operations import faint, styled from ..tui.operations import faint, styled
from ..tui.utils import report_error, report_unhandled_error
@lru_cache() @lru_cache()
@@ -43,6 +44,7 @@ DEFAULT_HINT_ALPHABET = string.digits + string.ascii_lowercase
DEFAULT_REGEX = r'(?m)^\s*(.+)\s*$' DEFAULT_REGEX = r'(?m)^\s*(.+)\s*$'
FILE_EXTENSION = r'\.(?:[a-zA-Z0-9]{2,7}|[ahcmo])(?!\.)' FILE_EXTENSION = r'\.(?:[a-zA-Z0-9]{2,7}|[ahcmo])(?!\.)'
PATH_REGEX = fr'(?:\S*?/[\r\S]+)|(?:\S[\r\S]*{FILE_EXTENSION})\b' PATH_REGEX = fr'(?:\S*?/[\r\S]+)|(?:\S[\r\S]*{FILE_EXTENSION})\b'
DEFAULT_LINENUM_REGEX = fr'(?P<path>{PATH_REGEX}):(?P<line>\d+)'
class Mark: class Mark:
@@ -63,6 +65,10 @@ class Mark:
self.is_hyperlink = is_hyperlink self.is_hyperlink = is_hyperlink
self.group_id = group_id self.group_id = group_id
def __repr__(self) -> str:
return (f'Mark(index={self.index!r}, start={self.start!r}, end={self.end!r},'
f' text={self.text!r}, groupdict={self.groupdict!r}, is_hyperlink={self.is_hyperlink!r}, group_id={self.group_id!r})')
@lru_cache(maxsize=2048) @lru_cache(maxsize=2048)
def encode_hint(num: int, alphabet: str) -> str: def encode_hint(num: int, alphabet: str) -> str:
@@ -117,6 +123,9 @@ def render(text: str, current_input: str, all_marks: Sequence[Mark], ignore_mark
class Hints(Handler): class Hints(Handler):
use_alternate_screen = False # disabled to avoid screen being blanked at exit causing flicker
overlay_ready_report_needed = True
def __init__(self, text: str, all_marks: Sequence[Mark], index_map: Dict[int, Mark], args: HintsCLIOptions): def __init__(self, text: str, all_marks: Sequence[Mark], index_map: Dict[int, Mark], args: HintsCLIOptions):
self.text, self.index_map = text, index_map self.text, self.index_map = text, index_map
self.alphabet = args.alphabet or DEFAULT_HINT_ALPHABET self.alphabet = args.alphabet or DEFAULT_HINT_ALPHABET
@@ -221,14 +230,14 @@ class Hints(Handler):
self.write(self.current_text) self.write(self.current_text)
def regex_finditer(pat: 'Pattern[str]', minimum_match_length: int, text: str) -> Generator[Tuple[int, int, Dict[str, str]], None, None]: def regex_finditer(pat: 'Pattern[str]', minimum_match_length: int, text: str) -> Iterator[Tuple[int, int, 're.Match[str]']]:
has_named_groups = bool(pat.groupindex) has_named_groups = bool(pat.groupindex)
for m in pat.finditer(text): for m in pat.finditer(text):
s, e = m.span(0 if has_named_groups else pat.groups) s, e = m.span(0 if has_named_groups else pat.groups)
while e > s + 1 and text[e-1] == '\0': while e > s + 1 and text[e-1] == '\0':
e -= 1 e -= 1
if e - s >= minimum_match_length: if e - s >= minimum_match_length:
yield s, e, m.groupdict() yield s, e, m
closing_bracket_map = {'(': ')', '[': ']', '{': '}', '<': '>', '*': '*', '"': '"', "'": "'", "": "", "": ""} closing_bracket_map = {'(': ')', '[': ']', '{': '}', '<': '>', '*': '*', '"': '"', "'": "'", "": "", "": ""}
@@ -314,16 +323,22 @@ def ip(text: str, s: int, e: int) -> Tuple[int, int]:
return s, e return s, e
def mark(pattern: str, post_processors: Iterable[PostprocessorFunc], text: str, args: HintsCLIOptions) -> Generator[Mark, None, None]: def mark(pattern: str, post_processors: Iterable[PostprocessorFunc], text: str, args: HintsCLIOptions) -> Iterator[Mark]:
pat = re.compile(pattern) pat = re.compile(pattern)
for idx, (s, e, groupdict) in enumerate(regex_finditer(pat, args.minimum_match_length, text)): sanitize_pat = re.compile('[\r\n\0]')
for idx, (s, e, match_object) in enumerate(regex_finditer(pat, args.minimum_match_length, text)):
try: try:
for func in post_processors: for func in post_processors:
s, e = func(text, s, e) s, e = func(text, s, e)
except InvalidMatch: except InvalidMatch:
continue continue
groupdict = match_object.groupdict()
mark_text = re.sub('[\r\n\0]', '', text[s:e]) for group_name in groupdict:
group_idx = pat.groupindex[group_name]
gs, ge = match_object.span(group_idx)
gs, ge = max(gs, s), min(ge, e)
groupdict[group_name] = sanitize_pat.sub('', text[gs:ge])
mark_text = sanitize_pat.sub('', text[s:e])
yield Mark(idx, s, e, mark_text, groupdict) yield Mark(idx, s, e, mark_text, groupdict)
@@ -396,7 +411,10 @@ def convert_text(text: str, cols: int) -> str:
appended = False appended = False
for line in full_line.split('\r'): for line in full_line.split('\r'):
if line: if line:
lines.append(line.ljust(cols, '\0')) line_sz = wcswidth(line)
if line_sz < cols:
line += '\0' * (cols - line_sz)
lines.append(line)
lines.append('\r') lines.append('\r')
appended = True appended = True
if appended: if appended:
@@ -416,7 +434,7 @@ def parse_input(text: str) -> str:
def linenum_marks(text: str, args: HintsCLIOptions, Mark: Type[Mark], extra_cli_args: Sequence[str], *a: Any) -> Generator[Mark, None, None]: def linenum_marks(text: str, args: HintsCLIOptions, Mark: Type[Mark], extra_cli_args: Sequence[str], *a: Any) -> Generator[Mark, None, None]:
regex = args.regex regex = args.regex
if regex == DEFAULT_REGEX: if regex == DEFAULT_REGEX:
regex = fr'(?P<path>{PATH_REGEX}):(?P<line>\d+)' regex = DEFAULT_LINENUM_REGEX
yield from mark(regex, [brackets, quotes], text, args) yield from mark(regex, [brackets, quotes], text, args)
@@ -500,7 +518,7 @@ def run(args: HintsCLIOptions, text: str, extra_cli_args: Sequence[str] = ()) ->
all_marks = tuple(mark(pattern, post_processors, text, args)) all_marks = tuple(mark(pattern, post_processors, text, args))
if not all_marks: if not all_marks:
none_of = {'url': 'URLs', 'hyperlink': 'hyperlinks'}.get(args.type, 'matches') none_of = {'url': 'URLs', 'hyperlink': 'hyperlinks'}.get(args.type, 'matches')
input(_('No {} found, press Enter to quit.').format(none_of)) report_error(_('No {} found.').format(none_of))
return None return None
largest_index = all_marks[-1].index largest_index = all_marks[-1].index
@@ -512,11 +530,7 @@ def run(args: HintsCLIOptions, text: str, extra_cli_args: Sequence[str] = ()) ->
m.index = largest_index - m.index + offset m.index = largest_index - m.index + offset
index_map = {m.index: m for m in all_marks} index_map = {m.index: m for m in all_marks}
except Exception: except Exception:
import traceback report_unhandled_error()
traceback.print_exc()
input('Press Enter to quit.')
raise SystemExit(1)
return run_loop(args, text, all_marks, index_map, extra_cli_args) return run_loop(args, text, all_marks, index_map, extra_cli_args)
@@ -525,12 +539,27 @@ OPTIONS = r'''
--program --program
type=list type=list
What program to use to open matched text. Defaults to the default open program What program to use to open matched text. Defaults to the default open program
for the operating system. Use a value of :file:`-` to paste the match into the for the operating system. Various special values are supported:
terminal window instead. A value of :file:`@` will copy the match to the
clipboard. A value of :file:`*` will copy the match to the primary selection :code:`-`
(on systems that support primary selections). A value of :file:`default` will paste the match into the terminal window.
run the default open program. Can be specified multiple times to run multiple
programs. :code:`@`
copy the match to the clipboard
:code:`*`
copy the match to the primary selection (on systems that support primary selections)
:code:`default`
run the default open program.
:code:`launch`
run :doc:`/launch` to open the program in a new kitty tab, window, overlay, etc.
For example::
--program "launch --type=tab vim"
Can be specified multiple times to run multiple programs.
--type --type
@@ -538,22 +567,22 @@ default=url
choices=url,regex,path,line,hash,word,linenum,hyperlink,ip choices=url,regex,path,line,hash,word,linenum,hyperlink,ip
The type of text to search for. A value of :code:`linenum` is special, it looks The type of text to search for. A value of :code:`linenum` is special, it looks
for error messages using the pattern specified with :option:`--regex`, which for error messages using the pattern specified with :option:`--regex`, which
must have the named groups, :code:`path` and :code:`line`. If not specified, must have the named groups: :code:`path` and :code:`line`. If not specified,
will look for :code:`path:line`. The :option:`--linenum-action` option will look for :code:`path:line`. The :option:`--linenum-action` option
controls where to display the selected error message, other options are ignored. controls where to display the selected error message, other options are ignored.
--regex --regex
default={default_regex} default={default_regex}
The regular expression to use when :option:`kitty +kitten hints --type`=regex. The regular expression to use when option :option:`--type` is set to
The regular expression is in python syntax. If you specify a numbered group in :code:`regex`, in python syntax. If you specify a numbered group in the regular
the regular expression only the group will be matched. This allow you to match expression, only the group will be matched. This allow you to match text
text ignoring a prefix/suffix, as needed. The default expression matches lines. ignoring a prefix/suffix, as needed. The default expression matches lines. To
To match text over multiple lines you should prefix the regular expression with match text over multiple lines, you should prefix the regular expression with
:code:`(?ms)`, which turns on MULTILINE and DOTALL modes for the regex engine. :code:`(?ms)`, which turns on MULTILINE and DOTALL modes for the regex engine.
If you specify named groups and a :option:`kitty +kitten hints --program` then If you specify named groups and a :option:`--program`, then the program will be
the program will be passed arguments corresponding to each named group of passed arguments corresponding to each named group of the form
the form key=value. :code:`key=value`.
--linenum-action --linenum-action
@@ -565,22 +594,22 @@ window, :code:`window` a new kitty window, :code:`tab` a new tab,
:code:`os_window` a new OS window and :code:`background` run in the background. :code:`os_window` a new OS window and :code:`background` run in the background.
The actual action is whatever arguments are provided to the kitten, for The actual action is whatever arguments are provided to the kitten, for
example: example:
:code:`kitty + kitten hints --type=linenum --linenum-action=tab vim +{line} {path}` :code:`kitty +kitten hints --type=linenum --linenum-action=tab vim +{line} {path}`
will open the matched path at the matched line number in vim in will open the matched path at the matched line number in vim in
a new kitty tab. Note that only when using :code:`self` are the special values for a new kitty tab. Note that in order to use :option:`--program` to copy or paste
:option:`kitty +kitten hints --program` to copy/paste the text respected. text, you need to use the special value :code:`self`.
--url-prefixes --url-prefixes
default=default default=default
Comma separated list of recognized URL prefixes. Defaults, to Comma separated list of recognized URL prefixes. Defaults to the list of
the list of prefixes defined in kitty.conf. prefixes defined by the :opt:`url_prefixes` option in :file:`kitty.conf`.
--word-characters --word-characters
Characters to consider as part of a word. In addition, all characters marked as Characters to consider as part of a word. In addition, all characters marked as
alphanumeric in the unicode database will be considered as word characters. alphanumeric in the Unicode database will be considered as word characters.
Defaults to the select_by_word_characters setting from kitty.conf. Defaults to the :opt:`select_by_word_characters` option from :file:`kitty.conf`.
--minimum-match-length --minimum-match-length
@@ -591,26 +620,26 @@ The minimum number of characters to consider a match.
--multiple --multiple
type=bool-set type=bool-set
Select multiple matches and perform the action on all of them together at the end. Select multiple matches and perform the action on all of them together at the
In this mode, press :kbd:`Esc` to finish selecting. end. In this mode, press :kbd:`Esc` to finish selecting.
--multiple-joiner --multiple-joiner
default=auto default=auto
String to use to join multiple selections when copying to the clipboard or String for joining multiple selections when copying to the clipboard or
inserting into the terminal. The special strings: "space", "newline", "empty", inserting into the terminal. The special values are: :code:`space` - a space
"json" and "auto" are interpreted as a space character, a newline an empty character, :code:`newline` - a newline, :code:`empty` - an empty joiner,
joiner, a JSON serialized list and an automatic choice, based on the type of :code:`json` - a JSON serialized list, :code:`auto` - an automatic choice, based
text being selected. In addition, integers are interpreted as zero-based on the type of text being selected. In addition, integers are interpreted as
indices into the list of selections. You can use 0 for the first selection and zero-based indices into the list of selections. You can use :code:`0` for the
-1 for the last. first selection and :code:`-1` for the last.
--add-trailing-space --add-trailing-space
default=auto default=auto
choices=auto,always,never choices=auto,always,never
Add trailing space after matched text. Defaults to auto, which adds the space Add trailing space after matched text. Defaults to :code:`auto`, which adds the
when used together with :option:`--multiple`. space when used together with :option:`--multiple`.
--hints-offset --hints-offset
@@ -621,45 +650,47 @@ greater than or equal to zero are respected.
--alphabet --alphabet
The list of characters to use for hints. The default is to use numbers and lowercase The list of characters to use for hints. The default is to use numbers and
English alphabets. Specify your preference as a string of characters. Note that lowercase English alphabets. Specify your preference as a string of characters.
unless you specify the hints offset as zero the first match will be highlighted with Note that you need to specify the :option:`--hints-offset` as zero to use the
the second character you specify. first character to highlight the first match, otherwise it will start with the
second character by default.
--ascending --ascending
type=bool-set type=bool-set
Have the hints increase from top to bottom instead of decreasing from top to bottom. Make the hints increase from top to bottom, instead of decreasing from top to
bottom.
--hints-foreground-color --hints-foreground-color
default=black default=black
type=str type=str
The foreground color for hints The foreground color for hints.
--hints-background-color --hints-background-color
default=green default=green
type=str type=str
The background color for hints The background color for hints.
--hints-text-color --hints-text-color
default=gray default=gray
type=str type=str
The foreground color for text pointed to by the hints The foreground color for text pointed to by the hints.
--customize-processing --customize-processing
Name of a python file in the kitty config directory which will be imported to provide Name of a python file in the kitty config directory which will be imported to
custom implementations for pattern finding and performing actions provide custom implementations for pattern finding and performing actions
on selected matches. See {hints_url} on selected matches. You can also specify absolute paths to load the script from
for details. You can also specify absolute paths to load the script from elsewhere. elsewhere. See {hints_url} for details.
--window-title --window-title
The window title for the hints window, default title is selected based on The title for the hints window, default title is based on the type of text being
the type of text being hinted. hinted.
'''.format( '''.format(
default_regex=DEFAULT_REGEX, default_regex=DEFAULT_REGEX,
line='{{line}}', path='{{path}}', line='{{line}}', path='{{path}}',
@@ -677,9 +708,7 @@ def main(args: List[str]) -> Optional[Dict[str, Any]]:
text = '' text = ''
if sys.stdin.isatty(): if sys.stdin.isatty():
if '--help' not in args and '-h' not in args: if '--help' not in args and '-h' not in args:
print('You must pass the text to be hinted on STDIN', file=sys.stderr) report_unhandled_error('You must pass the text to be hinted on STDIN')
input(_('Press Enter to quit'))
return None
else: else:
text = sys.stdin.buffer.read().decode('utf-8') text = sys.stdin.buffer.read().decode('utf-8')
sys.stdin = open(os.ctermid()) sys.stdin = open(os.ctermid())
@@ -687,34 +716,26 @@ def main(args: List[str]) -> Optional[Dict[str, Any]]:
opts, items = parse_hints_args(args[1:]) opts, items = parse_hints_args(args[1:])
except SystemExit as e: except SystemExit as e:
if e.code != 0: if e.code != 0:
print(e.args[0], file=sys.stderr) report_unhandled_error(e.args[0])
input(_('Press Enter to quit'))
return None return None
if items and not (opts.customize_processing or opts.type == 'linenum'): if items and not (opts.customize_processing or opts.type == 'linenum'):
print('Extra command line arguments present: {}'.format(' '.join(items)), file=sys.stderr) report_unhandled_error('Extra command line arguments present: {}'.format(' '.join(items)))
input(_('Press Enter to quit'))
try: try:
return run(opts, text, items) return run(opts, text, items)
except Exception: except Exception:
import traceback report_unhandled_error()
traceback.print_exc()
input(_('Press Enter to quit'))
return None return None
def linenum_process_result(data: Dict[str, Any]) -> Tuple[str, int]: def linenum_process_result(data: Dict[str, Any]) -> Tuple[str, int]:
pat = re.compile(r':(\d+)$') lnum_pat = re.compile(r'(:\d+)$')
for m, g in zip(data['match'], data['groupdicts']): for match, g in zip(data['match'], data['groupdicts']):
if m: path, line = g['path'], g['line']
path, line = g['path'], g['line'] if path and line:
# look for trailers on path of the for :number m = lnum_pat.search(path)
while True: if m is not None:
m = pat.search(path) line = m.group(1)[1:]
if m is None: path = path.rpartition(':')[0]
break
line = m.group(1)
path = path[:-len(m.group())]
return os.path.expanduser(path), int(line) return os.path.expanduser(path), int(line)
return '', -1 return '', -1
@@ -752,7 +773,7 @@ def linenum_handle_result(args: List[str], data: Dict[str, Any], target_window_i
}[action])(*cmd) }[action])(*cmd)
@result_handler(type_of_input='screen-ansi') @result_handler(type_of_input='screen-ansi', has_ready_notification=Hints.overlay_ready_report_needed)
def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int, boss: BossType) -> None: def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int, boss: BossType) -> None:
if data['customize_processing']: if data['customize_processing']:
m = load_custom_processor(data['customize_processing']) m = load_custom_processor(data['customize_processing'])
@@ -794,12 +815,13 @@ def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int,
if program == '-': if program == '-':
w = boss.window_id_map.get(target_window_id) w = boss.window_id_map.get(target_window_id)
if w is not None: if w is not None:
w.paste(joined_text()) w.paste_text(joined_text())
elif program == '@': elif program == '@':
set_clipboard_string(joined_text()) set_clipboard_string(joined_text())
elif program == '*': elif program == '*':
set_primary_selection(joined_text()) set_primary_selection(joined_text())
else: else:
from kitty.conf.utils import to_cmdline
cwd = data['cwd'] cwd = data['cwd']
program = get_options().open_url_with if program == 'default' else program program = get_options().open_url_with if program == 'default' else program
if text_type == 'hyperlink': if text_type == 'hyperlink':
@@ -808,12 +830,20 @@ def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int,
if w is not None: if w is not None:
w.open_url(m, hyperlink_id=1, cwd=cwd) w.open_url(m, hyperlink_id=1, cwd=cwd)
else: else:
launch_args = []
if isinstance(program, str) and program.startswith('launch '):
launch_args = to_cmdline(program)
launch_args.insert(1, '--cwd=' + cwd)
for m, groupdict in zip(matches, groupdicts): for m, groupdict in zip(matches, groupdicts):
if groupdict: if groupdict:
m = [] m = []
for k, v in groupdict.items(): for k, v in groupdict.items():
m.append('{}={}'.format(k, v or '')) m.append('{}={}'.format(k, v or ''))
boss.open_url(m, program, cwd=cwd) if launch_args:
w = boss.window_id_map.get(target_window_id)
boss.call_remote_control(active_window=w, args=tuple(launch_args + ([m] if isinstance(m, str) else m)))
else:
boss.open_url(m, program, cwd=cwd)
if __name__ == '__main__': if __name__ == '__main__':

71
kittens/hyperlinked_grep/main.py Normal file → Executable file
View File

@@ -20,15 +20,60 @@ def write_hyperlink(write: Callable[[bytes], None], url: bytes, line: bytes, fra
def main() -> None: def main() -> None:
if not sys.stdout.isatty() and '--pretty' not in sys.argv: i = 1
all_link_options = {'matching_lines', 'context_lines', 'file_headers'}
link_options = set()
delegate_to_rg = False
def parse_link_options(raw: str) -> None:
nonlocal delegate_to_rg
if not raw:
raise SystemExit('Must specify an argument for --kitten option')
p, _, s = raw.partition('=')
if p != 'hyperlink':
raise SystemExit(f'Unknown argument for --kitten: {raw}')
for option in s.split(','):
if option == 'all':
link_options.update(all_link_options)
delegate_to_rg = False
elif option == 'none':
delegate_to_rg = True
link_options.clear()
elif option not in all_link_options:
a = ', '.join(sorted(all_link_options))
raise SystemExit(f"hyperlink option must be one of all, none, {a}, not '{option}'")
else:
link_options.add(option)
delegate_to_rg = False
while i < len(sys.argv):
if sys.argv[i] == '--kitten':
next_item = '' if i + 1 >= len(sys.argv) else sys.argv[i + 1]
parse_link_options(next_item)
del sys.argv[i:i+2]
elif sys.argv[i].startswith('--kitten='):
parse_link_options(sys.argv[i][len('--kitten='):])
del sys.argv[i]
else:
i += 1
if not link_options: # Default to linking everything if no options given
link_options.update(all_link_options)
link_file_headers = 'file_headers' in link_options
link_context_lines = 'context_lines' in link_options
link_matching_lines = 'matching_lines' in link_options
if delegate_to_rg or (not sys.stdout.isatty() and '--pretty' not in sys.argv and '-p' not in sys.argv):
os.execlp('rg', 'rg', *sys.argv[1:]) os.execlp('rg', 'rg', *sys.argv[1:])
cmdline = ['rg', '--pretty', '--with-filename'] + sys.argv[1:] cmdline = ['rg', '--pretty', '--with-filename'] + sys.argv[1:]
p = subprocess.Popen(cmdline, stdout=subprocess.PIPE) try:
p = subprocess.Popen(cmdline, stdout=subprocess.PIPE)
except FileNotFoundError:
raise SystemExit('Could not find the rg executable in your PATH. Is ripgrep installed?')
assert p.stdout is not None assert p.stdout is not None
write: Callable[[bytes], None] = cast(Callable[[bytes], None], sys.stdout.buffer.write) write: Callable[[bytes], None] = cast(Callable[[bytes], None], sys.stdout.buffer.write)
sgr_pat = re.compile(br'\x1b\[.*?m') sgr_pat = re.compile(br'\x1b\[.*?m')
osc_pat = re.compile(b'\x1b\\].*?\x1b\\\\') osc_pat = re.compile(b'\x1b\\].*?\x1b\\\\')
num_pat = re.compile(br'^(\d+)[:-]') num_pat = re.compile(br'^(\d+)([:-])')
in_result: bytes = b'' in_result: bytes = b''
hostname = socket.gethostname().encode('utf-8') hostname = socket.gethostname().encode('utf-8')
@@ -40,23 +85,25 @@ def main() -> None:
if not clean_line: if not clean_line:
in_result = b'' in_result = b''
write(b'\n') write(b'\n')
continue elif in_result:
if in_result:
m = num_pat.match(clean_line) m = num_pat.match(clean_line)
if m is not None: if m is not None:
write_hyperlink(write, in_result, line, frag=m.group(1)) is_match_line = m.group(2) == b':'
else: if (is_match_line and link_matching_lines) or (not is_match_line and link_context_lines):
write(line) write_hyperlink(write, in_result, line, frag=m.group(1))
continue
write(line)
else: else:
if line.strip(): if line.strip():
path = quote_from_bytes(os.path.abspath(clean_line)).encode('utf-8') path = quote_from_bytes(os.path.abspath(clean_line)).encode('utf-8')
in_result = b'file://' + hostname + path in_result = b'file://' + hostname + path
write_hyperlink(write, in_result, line) if link_file_headers:
else: write_hyperlink(write, in_result, line)
write(line) continue
write(line)
except KeyboardInterrupt: except KeyboardInterrupt:
p.send_signal(signal.SIGINT) p.send_signal(signal.SIGINT)
except EOFError: except (EOFError, BrokenPipeError):
pass pass
finally: finally:
p.stdout.close() p.stdout.close()

62
kittens/icat/main.py Executable file → Normal file
View File

@@ -42,24 +42,24 @@ Horizontal alignment for the displayed image.
--place --place
Choose where on the screen to display the image. The image will Choose where on the screen to display the image. The image will be scaled to fit
be scaled to fit into the specified rectangle. The syntax for into the specified rectangle. The syntax for specifying rectangles is
specifying rectangles is <:italic:`width`>x<:italic:`height`>@<:italic:`left`>x<:italic:`top`>. <:italic:`width`>x<:italic:`height`>@<:italic:`left`>x<:italic:`top`>.
All measurements are in cells (i.e. cursor positions) with the All measurements are in cells (i.e. cursor positions) with the origin
origin :italic:`(0, 0)` at the top-left corner of the screen. :italic:`(0, 0)` at the top-left corner of the screen.
--scale-up --scale-up
type=bool-set type=bool-set
When used in combination with :option:`--place` it will cause images that When used in combination with :option:`--place` it will cause images that are
are smaller than the specified area to be scaled up to use as much smaller than the specified area to be scaled up to use as much of the specified
of the specified area as possible. area as possible.
--background --background
default=none default=none
Specify a background color, this will cause transparent images to be composited on Specify a background color, this will cause transparent images to be composited
top of the specified color. on top of the specified color.
--mirror --mirror
@@ -79,17 +79,18 @@ type=choices
choices=detect,file,stream choices=detect,file,stream
default=detect default=detect
Which mechanism to use to transfer images to the terminal. The default is to Which mechanism to use to transfer images to the terminal. The default is to
auto-detect. :italic:`file` means to use a temporary file and :italic:`stream` means to auto-detect. :italic:`file` means to use a temporary file and :italic:`stream`
send the data via terminal escape codes. Note that if you use the :italic:`file` means to send the data via terminal escape codes. Note that if you use the
transfer mode and you are connecting over a remote session then image display :italic:`file` transfer mode and you are connecting over a remote session then
will not work. image display will not work.
--detect-support --detect-support
type=bool-set type=bool-set
Detect support for image display in the terminal. If not supported, will exit Detect support for image display in the terminal. If not supported, will exit
with exit code 1, otherwise will exit with code 0 and print the supported with exit code 1, otherwise will exit with code 0 and print the supported
transfer mode to stderr, which can be used with the :option:`--transfer-mode` option. transfer mode to stderr, which can be used with the :option:`--transfer-mode`
option.
--detection-timeout --detection-timeout
@@ -101,17 +102,17 @@ detecting image display support.
--print-window-size --print-window-size
type=bool-set type=bool-set
Print out the window size as :italic:`widthxheight` (in pixels) and quit. This is a Print out the window size as <:italic:`width`>x<:italic:`height`> (in pixels) and quit. This is a
convenience method to query the window size if using :code:`kitty +kitten icat` from a convenience method to query the window size if using :code:`kitty +kitten icat`
scripting language that cannot make termios calls. from a scripting language that cannot make termios calls.
--stdin --stdin
type=choices type=choices
choices=detect,yes,no choices=detect,yes,no
default=detect default=detect
Read image data from stdin. The default is to do it automatically, when STDIN is not a terminal, Read image data from STDIN. The default is to do it automatically, when STDIN is
but you can turn it off or on explicitly, if needed. not a terminal, but you can turn it off or on explicitly, if needed.
--silent --silent
@@ -121,9 +122,9 @@ Do not print out anything to STDOUT during operation.
--z-index -z --z-index -z
default=0 default=0
Z-index of the image. When negative, text will be displayed on top of the image. Use Z-index of the image. When negative, text will be displayed on top of the image.
a double minus for values under the threshold for drawing images under cell background Use a double minus for values under the threshold for drawing images under cell
colors. For example, :code:`--1` evaluates as -1,073,741,825. background colors. For example, :code:`--1` evaluates as -1,073,741,825.
--loop -l --loop -l
@@ -352,7 +353,7 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil
else: else:
import struct import struct
use_number = max(1, struct.unpack('@I', os.urandom(4))[0]) use_number = max(1, struct.unpack('@I', os.urandom(4))[0])
with NamedTemporaryFile() as f: with NamedTemporaryFile(prefix='tty-graphics-protocol-') as f:
prefix = f.name prefix = f.name
frame_data = render_image( frame_data = render_image(
path, prefix, m, available_width, available_height, args.scale_up, path, prefix, m, available_width, available_height, args.scale_up,
@@ -404,7 +405,7 @@ def detect_support(wait_for: float = 10, silent: bool = False) -> bool:
parse_responses() parse_responses()
return 1 not in responses or 2 not in responses return 1 not in responses or 2 not in responses
with NamedTemporaryFile() as f: with NamedTemporaryFile(prefix='tty-graphics-protocol') as f:
f.write(b'abcd') f.write(b'abcd')
f.flush() f.flush()
gc = GraphicsCommand() gc = GraphicsCommand()
@@ -471,13 +472,13 @@ def process_single_item(
file_removed = False file_removed = False
try: try:
if isinstance(item, bytes): if isinstance(item, bytes):
with NamedTemporaryFile(prefix='stdin-image-data-', delete=False) as tf: with NamedTemporaryFile(prefix='tty-graphics-protocol-', delete=False) as tf:
tf.write(item) tf.write(item)
item = tf.name item = tf.name
is_tempfile = True is_tempfile = True
if url_pat is not None and url_pat.match(item) is not None: if url_pat is not None and url_pat.match(item) is not None:
from urllib.request import urlretrieve from urllib.request import urlretrieve
with NamedTemporaryFile(prefix='url-image-data-', delete=False) as tf: with NamedTemporaryFile(prefix='tty-graphics-protocol-', delete=False) as tf:
try: try:
with socket_timeout(30): with socket_timeout(30):
urlretrieve(item, filename=tf.name) urlretrieve(item, filename=tf.name)
@@ -515,7 +516,12 @@ def main(args: List[str] = sys.argv) -> None:
if cli_opts.print_window_size: if cli_opts.print_window_size:
screen_size_function.cache_clear() screen_size_function.cache_clear()
with open(os.ctermid()) as tty: with open(os.ctermid()) as tty:
ss = screen_size_function(tty)() try:
fd = tty.fileno()
except AttributeError:
# use default value for fd if ctermid is not available
fd = None
ss = screen_size_function(fd)()
print(f'{ss.width}x{ss.height}', end='') print(f'{ss.width}x{ss.height}', end='')
raise SystemExit(0) raise SystemExit(0)

View File

@@ -66,7 +66,7 @@ def query(cls: Type[Query]) -> Type[Query]:
class TerminalName(Query): class TerminalName(Query):
name: str = 'name' name: str = 'name'
override_query_name: str = 'name' override_query_name: str = 'name'
help_text: str = f'Terminal name ({names[0]})' help_text: str = f'Terminal name (e.g. :code:`{names[0]}`)'
@staticmethod @staticmethod
def get_result(opts: Options) -> str: def get_result(opts: Options) -> str:
@@ -76,7 +76,7 @@ class TerminalName(Query):
@query @query
class TerminalVersion(Query): class TerminalVersion(Query):
name: str = 'version' name: str = 'version'
help_text: str = 'Terminal version, for e.g.: 0.19.2' help_text: str = f'Terminal version (e.g. :code:`{str_version}`)'
@staticmethod @staticmethod
def get_result(opts: Options) -> str: def get_result(opts: Options) -> str:
@@ -86,7 +86,7 @@ class TerminalVersion(Query):
@query @query
class AllowHyperlinks(Query): class AllowHyperlinks(Query):
name: str = 'allow_hyperlinks' name: str = 'allow_hyperlinks'
help_text: str = 'The :opt:`setting <allow_hyperlinks>` for allowing hyperlinks can be yes, no or ask' help_text: str = 'The config option :opt:`allow_hyperlinks` in :file:`kitty.conf` for allowing hyperlinks can be :code:`yes`, :code:`no` or :code:`ask`'
@staticmethod @staticmethod
def get_result(opts: Options) -> str: def get_result(opts: Options) -> str:
@@ -154,7 +154,7 @@ class FontSize(Query):
@query @query
class ClipboardControl(Query): class ClipboardControl(Query):
name: str = 'clipboard_control' name: str = 'clipboard_control'
help_text: str = 'The :opt:`setting <clipboard_control>` for allowing reads/writes to/from the clipboard' help_text: str = 'The config option :opt:`clipboard_control` in :file:`kitty.conf` for allowing reads/writes to/from the clipboard'
@staticmethod @staticmethod
def get_result(opts: Options) -> str: def get_result(opts: Options) -> str:
@@ -173,17 +173,22 @@ def do_queries(queries: Iterable[str], cli_opts: QueryTerminalCLIOptions) -> Dic
actions = tuple(all_queries[x]() for x in queries) actions = tuple(all_queries[x]() for x in queries)
qstring = ''.join(a.query_code() for a in actions) qstring = ''.join(a.query_code() for a in actions)
received = b'' received = b''
pat = re.compile(rb'\x1b\[\?.+?c')
def more_needed(data: bytes) -> bool: def more_needed(data: bytes) -> bool:
nonlocal received nonlocal received
received += data received += data
has_da1_response = pat.search(received) is not None
if has_da1_response:
return False
for a in actions: for a in actions:
if a.more_needed(received): if a.more_needed(received):
return True return True
return False return has_da1_response
with TTYIO() as ttyio: with TTYIO() as ttyio:
ttyio.send(qstring) ttyio.send(qstring)
ttyio.send('\x1b[c') # DA1 query https://vt100.net/docs/vt510-rm/DA1.html
ttyio.recv(more_needed, timeout=cli_opts.wait_for) ttyio.recv(more_needed, timeout=cli_opts.wait_for)
return {a.name: a.output_line() for a in actions} return {a.name: a.output_line() for a in actions}
@@ -200,30 +205,28 @@ querying it.
help_text = '''\ help_text = '''\
Query the terminal this kitten is run in for various Query the terminal this kitten is run in for various capabilities. This sends
capabilities. This sends escape codes to the terminal escape codes to the terminal and based on its response prints out data about
and based on its response prints out data about supported supported capabilities. Note that this is a blocking operation, since it has to
capabilities. Note that this is a blocking operation, since wait for a response from the terminal. You can control the maximum wait time via
it has to wait for a response from the terminal. You can control the :code:`--wait-for` option.
the maximum wait time via the ``--wait-for`` option.
The output is lines of the form:: The output is lines of the form::
query: data query: data
If a particular query is unsupported by the running kitty version, If a particular :italic:`query` is unsupported by the running kitty version, the
the data will be blank. :italic:`data` will be blank.
Note that when calling this from another program, be very Note that when calling this from another program, be very careful not to perform
careful not to perform any I/O on the terminal device any I/O on the terminal device until this kitten exits.
until the kitten exits.
Available queries are: Available queries are:
{} {}
'''.format('\n'.join( '''.format('\n'.join(
f'``{name}``\n {c.help_text}\n' for name, c in all_queries.items())) f':code:`{name}`:\n {c.help_text}\n' for name, c in all_queries.items()))
usage = '[query1 query2 ...]' usage = '[query1 query2 ...]'

View File

@@ -27,6 +27,9 @@ from ..tui.operations import (
from ..tui.utils import get_key_press from ..tui.utils import get_key_press
is_ssh_kitten_sentinel = '!#*&$#($ssh-kitten)(##$'
def key(x: str) -> str: def key(x: str) -> str:
return styled(x, bold=True, fg='green') return styled(x, bold=True, fg='green')
@@ -53,10 +56,9 @@ The data used to connect over ssh.
def show_error(msg: str) -> None: def show_error(msg: str) -> None:
print(styled(msg, fg='red')) print(styled(msg, fg='red'), file=sys.stderr)
print() print()
print('Press any key to exit...') print('Press any key to quit', flush=True)
sys.stdout.flush()
with raw_mode(): with raw_mode():
while True: while True:
try: try:
@@ -112,15 +114,25 @@ class ControlMaster:
self.remote_path = remote_path self.remote_path = remote_path
self.dest = dest self.dest = dest
self.tdir = '' self.tdir = ''
self.last_error_log = ''
self.cmd_prefix = cmd = [ self.cmd_prefix = cmd = [
conn_data.binary, '-o', f'ControlPath=~/.ssh/kitty-master-{os.getpid()}-%r@%h:%p', conn_data.binary, '-o', f'ControlPath=~/.ssh/kitty-master-{os.getpid()}-%r@%h:%p',
'-o', 'TCPKeepAlive=yes', '-o', 'ControlPersist=yes' '-o', 'TCPKeepAlive=yes', '-o', 'ControlPersist=yes'
] ]
if conn_data.port: self.is_ssh_kitten = conn_data.binary is is_ssh_kitten_sentinel
cmd.extend(['-p', str(conn_data.port)]) if self.is_ssh_kitten:
if conn_data.identity_file: del cmd[:]
cmd.extend(['-i', conn_data.identity_file]) self.batch_cmd_prefix = cmd
self.batch_cmd_prefix = cmd + ['-o', 'BatchMode=yes'] sk_cmdline = json.loads(conn_data.identity_file)
while '-t' in sk_cmdline:
sk_cmdline.remove('-t')
cmd.extend(sk_cmdline[:-2])
else:
if conn_data.port:
cmd.extend(['-p', str(conn_data.port)])
if conn_data.identity_file:
cmd.extend(['-i', conn_data.identity_file])
self.batch_cmd_prefix = cmd + ['-o', 'BatchMode=yes']
def check_call(self, cmd: List[str]) -> None: def check_call(self, cmd: List[str]) -> None:
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL)
@@ -130,31 +142,37 @@ class ControlMaster:
raise Exception(f'The ssh command: {shlex.join(cmd)} failed with exit code {p.returncode} and output: {out}') raise Exception(f'The ssh command: {shlex.join(cmd)} failed with exit code {p.returncode} and output: {out}')
def __enter__(self) -> 'ControlMaster': def __enter__(self) -> 'ControlMaster':
self.check_call( if not self.is_ssh_kitten:
self.cmd_prefix + ['-o', 'ControlMaster=auto', '-fN', self.conn_data.hostname]) self.check_call(
self.check_call( self.cmd_prefix + ['-o', 'ControlMaster=auto', '-fN', self.conn_data.hostname])
self.batch_cmd_prefix + ['-O', 'check', self.conn_data.hostname]) self.check_call(
self.batch_cmd_prefix + ['-O', 'check', self.conn_data.hostname])
if not self.dest: if not self.dest:
self.tdir = tempfile.mkdtemp() self.tdir = tempfile.mkdtemp()
self.dest = os.path.join(self.tdir, os.path.basename(self.remote_path)) self.dest = os.path.join(self.tdir, os.path.basename(self.remote_path))
return self return self
def __exit__(self, *a: Any) -> None: def __exit__(self, *a: Any) -> None:
subprocess.Popen( if not self.is_ssh_kitten:
self.batch_cmd_prefix + ['-O', 'exit', self.conn_data.hostname], subprocess.Popen(
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL self.batch_cmd_prefix + ['-O', 'exit', self.conn_data.hostname],
).wait() stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL
).wait()
if self.tdir: if self.tdir:
shutil.rmtree(self.tdir) shutil.rmtree(self.tdir)
@property @property
def is_alive(self) -> bool: def is_alive(self) -> bool:
if self.is_ssh_kitten:
return True
return subprocess.Popen( return subprocess.Popen(
self.batch_cmd_prefix + ['-O', 'check', self.conn_data.hostname], self.batch_cmd_prefix + ['-O', 'check', self.conn_data.hostname],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL
).wait() == 0 ).wait() == 0
def check_hostname_matches(self) -> bool: def check_hostname_matches(self) -> bool:
if self.is_ssh_kitten:
return True
cp = subprocess.run(self.batch_cmd_prefix + [self.conn_data.hostname, 'hostname', '-f'], stdout=subprocess.PIPE, cp = subprocess.run(self.batch_cmd_prefix + [self.conn_data.hostname, 'hostname', '-f'], stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL) stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL)
if cp.returncode == 0: if cp.returncode == 0:
@@ -181,21 +199,35 @@ class ControlMaster:
return response == 'y' return response == 'y'
return True return True
def show_error(self, msg: str) -> None:
if self.last_error_log:
print(self.last_error_log, file=sys.stderr)
self.last_error_log = ''
show_error(msg)
def download(self) -> bool: def download(self) -> bool:
cmdline = self.batch_cmd_prefix + [self.conn_data.hostname, 'cat', shlex.quote(self.remote_path)]
with open(self.dest, 'wb') as f: with open(self.dest, 'wb') as f:
return subprocess.run( cp = subprocess.run(cmdline, stdout=f, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL)
self.batch_cmd_prefix + [self.conn_data.hostname, 'cat', self.remote_path], if cp.returncode != 0:
stdout=f, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL self.last_error_log = f'The command: {shlex.join(cmdline)} failed\n' + cp.stderr.decode()
).returncode == 0 return False
return True
def upload(self, suppress_output: bool = True) -> bool: def upload(self, suppress_output: bool = True) -> bool:
cmd_prefix = self.cmd_prefix if suppress_output else self.batch_cmd_prefix cmd_prefix = self.cmd_prefix if suppress_output else self.batch_cmd_prefix
cmd = cmd_prefix + [self.conn_data.hostname, 'cat', '>', self.remote_path] cmd = cmd_prefix + [self.conn_data.hostname, 'cat', '>', shlex.quote(self.remote_path)]
if not suppress_output: if not suppress_output:
print(' '.join(map(shlex.quote, cmd))) print(shlex.join(cmd))
redirect = subprocess.DEVNULL if suppress_output else None
with open(self.dest, 'rb') as f: with open(self.dest, 'rb') as f:
return subprocess.run(cmd, stdout=redirect, stderr=redirect, stdin=f).returncode == 0 if suppress_output:
cp = subprocess.run(cmd, stdin=f, capture_output=True)
if cp.returncode == 0:
return True
self.last_error_log = f'The command: {shlex.join(cmd)} failed\n' + cp.stdout.decode()
else:
return subprocess.run(cmd, stdin=f).returncode == 0
return False
Result = Optional[str] Result = Optional[str]
@@ -208,7 +240,7 @@ def main(args: List[str]) -> Result:
except SystemExit as e: except SystemExit as e:
if e.code != 0: if e.code != 0:
print(e.args[0]) print(e.args[0])
input('Press enter to quit...') input('Press Enter to quit')
raise SystemExit(e.code) raise SystemExit(e.code)
try: try:
@@ -236,14 +268,14 @@ def save_as(conn_data: SSHConnectionData, remote_path: str, cli_opts: RemoteFile
last_used_path = tempfile.gettempdir() last_used_path = tempfile.gettempdir()
last_used_file = os.path.join(last_used_path, os.path.basename(remote_path)) last_used_file = os.path.join(last_used_path, os.path.basename(remote_path))
print( print(
'Where do you wish to save the file? Leaving it blank will save it as:', 'Where do you want to save the file? Leaving it blank will save it as:',
styled(last_used_file, fg='yellow') styled(last_used_file, fg='yellow')
) )
print('Relative paths will be resolved from:', styled(os.getcwd(), fg_intense=True, bold=True)) print('Relative paths will be resolved from:', styled(os.getcwd(), fg_intense=True, bold=True))
print() print()
from ..tui.path_completer import PathCompleter from ..tui.path_completer import get_path
try: try:
dest = PathCompleter().input() dest = get_path()
except (KeyboardInterrupt, EOFError): except (KeyboardInterrupt, EOFError):
return return
if dest: if dest:
@@ -278,11 +310,15 @@ def save_as(conn_data: SSHConnectionData, remote_path: str, cli_opts: RemoteFile
with ControlMaster(conn_data, remote_path, cli_opts, dest=dest) as master: with ControlMaster(conn_data, remote_path, cli_opts, dest=dest) as master:
if master.check_hostname_matches(): if master.check_hostname_matches():
if not master.download(): if not master.download():
show_error('Failed to copy file from remote machine') master.show_error('Failed to copy file from remote machine')
def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result: def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result:
conn_data = SSHConnectionData(*json.loads(cli_opts.ssh_connection_data or '')) cli_data = json.loads(cli_opts.ssh_connection_data or '')
if cli_data and cli_data[0] == is_ssh_kitten_sentinel:
conn_data = SSHConnectionData(is_ssh_kitten_sentinel, cli_data[-1], -1, identity_file=json.dumps(cli_data[1:]))
else:
conn_data = SSHConnectionData(*cli_data)
remote_path = cli_opts.path or '' remote_path = cli_opts.path or ''
if action == 'open': if action == 'open':
print('Opening', cli_opts.path, 'from', cli_opts.hostname) print('Opening', cli_opts.path, 'from', cli_opts.hostname)
@@ -291,7 +327,7 @@ def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result:
if master.check_hostname_matches(): if master.check_hostname_matches():
if master.download(): if master.download():
return dest return dest
show_error('Failed to copy file from remote machine') master.show_error('Failed to copy file from remote machine')
elif action == 'edit': elif action == 'edit':
print('Editing', cli_opts.path, 'from', cli_opts.hostname) print('Editing', cli_opts.path, 'from', cli_opts.hostname)
editor = get_editor() editor = get_editor()
@@ -299,7 +335,7 @@ def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result:
if not master.check_hostname_matches(): if not master.check_hostname_matches():
return None return None
if not master.download(): if not master.download():
show_error(f'Failed to download {remote_path}') master.show_error(f'Failed to download {remote_path}')
return None return None
mtime = os.path.getmtime(master.dest) mtime = os.path.getmtime(master.dest)
print(reset_terminal(), end='', flush=True) print(reset_terminal(), end='', flush=True)
@@ -314,9 +350,9 @@ def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result:
print(reset_terminal(), end='', flush=True) print(reset_terminal(), end='', flush=True)
if master.is_alive: if master.is_alive:
if not master.upload(suppress_output=False): if not master.upload(suppress_output=False):
show_error(f'Failed to upload {remote_path}') master.show_error(f'Failed to upload {remote_path}')
else: else:
show_error(f'Failed to upload {remote_path}, SSH master process died') master.show_error(f'Failed to upload {remote_path}, SSH master process died')
elif action == 'save': elif action == 'save':
print('Saving', cli_opts.path, 'from', cli_opts.hostname) print('Saving', cli_opts.path, 'from', cli_opts.hostname)
save_as(conn_data, remote_path, cli_opts) save_as(conn_data, remote_path, cli_opts)

View File

@@ -9,10 +9,10 @@ from contextlib import contextmanager
from functools import partial from functools import partial
from typing import TYPE_CHECKING, Any, Dict, FrozenSet, Generator, List, cast from typing import TYPE_CHECKING, Any, Dict, FrozenSet, Generator, List, cast
from kitty.constants import list_kitty_resources
from kitty.types import run_once from kitty.types import run_once
from kitty.utils import resolve_abs_or_config_path from kitty.utils import resolve_abs_or_config_path
aliases = {'url_hints': 'hints'} aliases = {'url_hints': 'hints'}
if TYPE_CHECKING: if TYPE_CHECKING:
from kitty.conf.types import Definition from kitty.conf.types import Definition
@@ -69,6 +69,7 @@ def create_kitten_handler(kitten: str, orig_args: List[str]) -> Any:
ans = partial(m['end'], [kitten] + orig_args) ans = partial(m['end'], [kitten] + orig_args)
setattr(ans, 'type_of_input', getattr(m['end'], 'type_of_input', None)) setattr(ans, 'type_of_input', getattr(m['end'], 'type_of_input', None))
setattr(ans, 'no_ui', getattr(m['end'], 'no_ui', False)) setattr(ans, 'no_ui', getattr(m['end'], 'no_ui', False))
setattr(ans, 'has_ready_notification', getattr(m['end'], 'has_ready_notification', False))
return ans return ans
@@ -85,42 +86,31 @@ def launch(args: List[str]) -> None:
del args[:2] del args[:2]
args = [kitten] + args args = [kitten] + args
os.environ['KITTY_CONFIG_DIRECTORY'] = config_dir os.environ['KITTY_CONFIG_DIRECTORY'] = config_dir
from kittens.tui.operations import clear_screen, reset_mode, Mode
set_debug(kitten) set_debug(kitten)
m = import_kitten_main_module(config_dir, kitten) m = import_kitten_main_module(config_dir, kitten)
try: try:
result = m['start'](args) result = m['start'](args)
finally: finally:
sys.stdin = sys.__stdin__ sys.stdin = sys.__stdin__
print(reset_mode(Mode.ALTERNATE_SCREEN) + clear_screen(), end='')
if result is not None: if result is not None:
import json import json
data = json.dumps(result) import base64
print('OK:', len(data), data) data = base64.b85encode(json.dumps(result).encode('utf-8'))
sys.stdout.buffer.write(b'\x1bP@kitty-kitten-result|')
sys.stdout.buffer.write(data)
sys.stdout.buffer.write(b'\x1b\\')
sys.stderr.flush() sys.stderr.flush()
sys.stdout.flush() sys.stdout.flush()
def deserialize(output: str) -> Any:
import json
if output.startswith('OK: '):
try:
prefix, sz, rest = output.split(' ', 2)
return json.loads(rest[:int(sz)])
except Exception:
raise ValueError(f'Failed to parse kitten output: {output!r}')
def run_kitten(kitten: str, run_name: str = '__main__') -> None: def run_kitten(kitten: str, run_name: str = '__main__') -> None:
import runpy import runpy
original_kitten_name = kitten original_kitten_name = kitten
kitten = resolved_kitten(kitten) kitten = resolved_kitten(kitten)
set_debug(kitten) set_debug(kitten)
try: if kitten in all_kitten_names():
runpy.run_module(f'kittens.{kitten}.main', run_name=run_name) runpy.run_module(f'kittens.{kitten}.main', run_name=run_name)
return return
except ImportError:
pass
# Look for a custom kitten # Look for a custom kitten
if not kitten.endswith('.py'): if not kitten.endswith('.py'):
kitten += '.py' kitten += '.py'
@@ -132,12 +122,15 @@ def run_kitten(kitten: str, run_name: str = '__main__') -> None:
print(kitten, file=sys.stderr) print(kitten, file=sys.stderr)
raise SystemExit(f'No kitten named {original_kitten_name}') raise SystemExit(f'No kitten named {original_kitten_name}')
m = runpy.run_path(path, init_globals={'sys': sys, 'os': os}, run_name='__run_kitten__') m = runpy.run_path(path, init_globals={'sys': sys, 'os': os}, run_name='__run_kitten__')
m['main'](sys.argv) from kitty.fast_data_types import set_options
try:
m['main'](sys.argv)
finally:
set_options(None)
@run_once @run_once
def all_kitten_names() -> FrozenSet[str]: def all_kitten_names() -> FrozenSet[str]:
from kitty.constants import list_kitty_resources
ans = [] ans = []
for name in list_kitty_resources('kittens'): for name in list_kitty_resources('kittens'):
if '__' not in name and '.' not in name and name != 'tui': if '__' not in name and '.' not in name and name != 'tui':
@@ -186,4 +179,4 @@ def main() -> None:
print('Unhandled exception running kitten:') print('Unhandled exception running kitten:')
import traceback import traceback
traceback.print_exc() traceback.print_exc()
input('Press Enter to quit...') input('Press Enter to quit')

View File

@@ -1,13 +1,17 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import json
import os import os
import sys import sys
import termios
from contextlib import suppress from contextlib import suppress
from typing import List from typing import List
from kitty.cli import parse_args from kitty.cli import parse_args
from kitty.cli_stub import ErrorCLIOptions from kitty.cli_stub import ErrorCLIOptions
from kitty.fast_data_types import open_tty
from kitty.utils import hold_till_enter, no_echo, write_all
from ..tui.operations import styled from ..tui.operations import styled
@@ -21,13 +25,35 @@ The title for the error message.
def real_main(args: List[str]) -> None: def real_main(args: List[str]) -> None:
msg = 'Show an error message' msg = 'Show an error message'
cli_opts, items = parse_args(args[1:], OPTIONS, '', msg, 'hints', result_class=ErrorCLIOptions) cli_opts, items = parse_args(args[1:], OPTIONS, '', msg, 'hints', result_class=ErrorCLIOptions)
error_message = sys.stdin.buffer.read().decode('utf-8') data = json.loads(sys.stdin.buffer.read())
sys.stdin = open(os.ctermid()) error_message = data['msg']
print(styled(cli_opts.title, fg_intense=True, fg='red', bold=True)) if cli_opts.title:
print() print(styled(cli_opts.title, fg_intense=True, fg='red', bold=True))
print(error_message) print()
print() print(error_message, flush=True)
input('Press Enter to close.') if data.get('tb'):
import select
from kittens.tui.operations import init_state, set_cursor_visible
fd, original_termios = open_tty()
msg = '\n\r\x1b[1;32mPress e to see detailed traceback or any other key to exit\x1b[m'
write_all(fd, msg)
write_all(fd, init_state(alternate_screen=False, kitty_keyboard_mode=False) + set_cursor_visible(False))
with no_echo(fd):
termios.tcdrain(fd)
while True:
rd = select.select([fd], [], [])[0]
if not rd:
break
q = os.read(fd, 1)
if q in b'eE':
break
return
if data.get('tb'):
tb = data['tb']
for ln in tb.splitlines():
print('\r\n', ln, sep='', end='')
print(flush=True)
hold_till_enter()
def main(args: List[str]) -> None: def main(args: List[str]) -> None:
@@ -37,7 +63,7 @@ def main(args: List[str]) -> None:
except Exception: except Exception:
import traceback import traceback
traceback.print_exc() traceback.print_exc()
input('Press Enter to close.') input('Press Enter to close')
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -34,7 +34,7 @@ def format_mods(mods: int) -> str:
class KeysHandler(Handler): class KeysHandler(Handler):
def initialize(self) -> None: def initialize(self) -> None:
self.cmd.set_window_title('Kitty extended keyboard protocol demo') self.cmd.set_window_title('kitty extended keyboard protocol demo')
self.cmd.set_cursor_visible(False) self.cmd.set_cursor_visible(False)
self.print('Press any keys - Ctrl+C or Ctrl+D will terminate') self.print('Press any keys - Ctrl+C or Ctrl+D will terminate')
@@ -47,7 +47,10 @@ class KeysHandler(Handler):
mods = format_mods(key_event.mods) mods = format_mods(key_event.mods)
if mods: if mods:
mods += '+' mods += '+'
key = f'{mods}{key_event.key} ' kk = key_event.key
if kk == ' ':
kk = 'SPACE'
key = f'{mods}{kk} '
self.cmd.colored(key, 'green') self.cmd.colored(key, 'green')
self.cmd.colored(etype + ' ', 'yellow') self.cmd.colored(etype + ' ', 'yellow')
self.cmd.styled(key_event.text, italic=True) self.cmd.styled(key_event.text, italic=True)

View File

@@ -24,7 +24,7 @@ def print_key(raw: bytearray) -> None:
unix += chr(ch) unix += chr(ch)
print(unix + '\t\t', end='') print(unix + '\t\t', end='')
for ch in raw: for ch in raw:
x = chr(ch).encode('ascii') x = chr(ch).encode('utf-8')
print(styled(repr(x)[2:-1], fg='yellow'), end='') print(styled(repr(x)[2:-1], fg='yellow'), end='')
print(end='\r\n', flush=True) print(end='\r\n', flush=True)
@@ -45,7 +45,7 @@ def read_keys() -> None:
def legacy_main() -> None: def legacy_main() -> None:
print('Press any keys - Ctrl-D will terminate this program', end='\r\n', flush=True) print('Press any keys - Ctrl+D will terminate this program', end='\r\n', flush=True)
print(styled('UNIX', italic=True, fg='green'), styled('send_text', italic=True, fg='green'), sep='\t\t', end='\r\n') print(styled('UNIX', italic=True, fg='green'), styled('send_text', italic=True, fg='green'), sep='\t\t', end='\r\n')
with raw_mode(): with raw_mode():
@@ -57,13 +57,16 @@ OPTIONS = r'''
default=normal default=normal
type=choices type=choices
choices=normal,application,kitty,unchanged choices=normal,application,kitty,unchanged
The keyboard mode to use when showing keys. "normal" mode is with DECCKM reset and "application" mode is with The keyboard mode to use when showing keys. :code:`normal` mode is with DECCKM
DECCKM set. "kitty" is the full kitty extended keyboard protocol. reset and :code:`application` mode is with DECCKM set. :code:`kitty` is the full
kitty extended keyboard protocol.
'''.format '''.format
help_text = 'Show the codes generated by the terminal for key presses in various keyboard modes'
usage = ''
def main(args: List[str]) -> None: def main(args: List[str]) -> None:
cli_opts, items = parse_args(args[1:], OPTIONS, '', '', 'kitty +kitten show_key', result_class=ShowKeyCLIOptions) cli_opts, items = parse_args(args[1:], OPTIONS, '', help_text, 'kitty +kitten show_key', result_class=ShowKeyCLIOptions)
if cli_opts.key_mode == 'kitty': if cli_opts.key_mode == 'kitty':
from .kitty_mode import main as kitty_main from .kitty_mode import main as kitty_main
return kitty_main() return kitty_main()
@@ -78,3 +81,8 @@ def main(args: List[str]) -> None:
if __name__ == '__main__': if __name__ == '__main__':
main(sys.argv) main(sys.argv)
elif __name__ == '__doc__':
cd = sys.cli_docs # type: ignore
cd['usage'] = usage
cd['options'] = OPTIONS
cd['help_text'] = help_text

View File

@@ -8,6 +8,7 @@ from typing import Callable, Dict, Iterable, Iterator, Sequence, Tuple
from kitty.complete import Completions, complete_files_and_dirs, debug from kitty.complete import Completions, complete_files_and_dirs, debug
from kitty.types import run_once from kitty.types import run_once
from .utils import ssh_options
debug debug
@@ -71,47 +72,6 @@ def known_hosts() -> Tuple[str, ...]:
return tuple(sorted(filter(lambda x: '*' not in x and '[' not in x, set(iter_known_hosts())))) return tuple(sorted(filter(lambda x: '*' not in x and '[' not in x, set(iter_known_hosts()))))
@run_once
def ssh_options() -> Dict[str, str]:
try:
p = subprocess.run(['ssh'], stderr=subprocess.PIPE, encoding='utf-8')
raw = p.stderr or ''
except FileNotFoundError:
return {
'4': '', '6': '', 'A': '', 'a': '', 'C': '', 'f': '', 'G': '', 'g': '', 'K': '', 'k': '',
'M': '', 'N': '', 'n': '', 'q': '', 's': '', 'T': '', 't': '', 'V': '', 'v': '', 'X': '',
'x': '', 'Y': '', 'y': '', 'B': 'bind_interface', 'b': 'bind_address', 'c': 'cipher_spec',
'D': '[bind_address:]port', 'E': 'log_file', 'e': 'escape_char', 'F': 'configfile', 'I': 'pkcs11',
'i': 'identity_file', 'J': '[user@]host[:port]', 'L': 'address', 'l': 'login_name', 'm': 'mac_spec',
'O': 'ctl_cmd', 'o': 'option', 'p': 'port', 'Q': 'query_option', 'R': 'address',
'S': 'ctl_path', 'W': 'host:port', 'w': 'local_tun[:remote_tun]'
}
ans: Dict[str, str] = {}
pos = 0
while True:
pos = raw.find('[', pos)
if pos < 0:
break
num = 1
epos = pos
while num > 0:
epos += 1
if raw[epos] not in '[]':
continue
num += 1 if raw[epos] == '[' else -1
q = raw[pos+1:epos]
pos = epos
if len(q) < 2 or q[0] != '-':
continue
if ' ' in q:
opt, desc = q.split(' ', 1)
ans[opt[1:]] = desc
else:
ans.update(dict.fromkeys(q[1:], ''))
return ans
# option help {{{ # option help {{{
@run_once @run_once
def option_help_map() -> Dict[str, str]: def option_help_map() -> Dict[str, str]:
@@ -331,6 +291,7 @@ def complete_option(ans: Completions, prefix: str = '-') -> None:
def complete(ans: Completions, words: Sequence[str], new_word: bool) -> None: def complete(ans: Completions, words: Sequence[str], new_word: bool) -> None:
options = ssh_options() options = ssh_options()
expecting_arg = False expecting_arg = False
seen_destination = False
types = ['' for i in range(len(words))] types = ['' for i in range(len(words))]
for i, word in enumerate(words): for i, word in enumerate(words):
if expecting_arg: if expecting_arg:
@@ -342,8 +303,10 @@ def complete(ans: Completions, words: Sequence[str], new_word: bool) -> None:
if len(word) == 2 and options.get(word[1]): if len(word) == 2 and options.get(word[1]):
expecting_arg = True expecting_arg = True
continue continue
if seen_destination:
break
types[i] = 'destination' types[i] = 'destination'
break seen_destination = True
if new_word: if new_word:
if words: if words:
if expecting_arg: if expecting_arg:

74
kittens/ssh/config.py Normal file
View File

@@ -0,0 +1,74 @@
#!/usr/bin/env python
# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>
import fnmatch
import os
from typing import Any, Dict, Iterable, Optional
from kitty.conf.utils import (
load_config as _load_config, parse_config_base, resolve_config
)
from kitty.constants import config_dir
from .options.types import Options as SSHOptions, defaults
SYSTEM_CONF = '/etc/xdg/kitty/ssh.conf'
defconf = os.path.join(config_dir, 'ssh.conf')
def host_matches(mpat: str, hostname: str, username: str) -> bool:
for pat in mpat.split():
upat = '*'
if '@' in pat:
upat, pat = pat.split('@', 1)
if fnmatch.fnmatchcase(hostname, pat) and fnmatch.fnmatchcase(username, upat):
return True
return False
def load_config(*paths: str, overrides: Optional[Iterable[str]] = None, hostname: str = '!', username: str = '') -> SSHOptions:
from .options.parse import (
create_result_dict, merge_result_dicts, parse_conf_item
)
from .options.utils import (
first_seen_positions, get_per_hosts_dict, init_results_dict
)
def merge_dicts(base: Dict[str, Any], vals: Dict[str, Any]) -> Dict[str, Any]:
base_phd = get_per_hosts_dict(base)
vals_phd = get_per_hosts_dict(vals)
for hostname in base_phd:
vals_phd[hostname] = merge_result_dicts(base_phd[hostname], vals_phd.get(hostname, {}))
ans: Dict[str, Any] = vals_phd.pop(vals['hostname'])
ans['per_host_dicts'] = vals_phd
return ans
def parse_config(lines: Iterable[str]) -> Dict[str, Any]:
ans: Dict[str, Any] = init_results_dict(create_result_dict())
parse_config_base(lines, parse_conf_item, ans)
return ans
overrides = tuple(overrides) if overrides is not None else ()
first_seen_positions.clear()
first_seen_positions['*'] = 0
opts_dict, paths = _load_config(
defaults, parse_config, merge_dicts, *paths, overrides=overrides, initialize_defaults=init_results_dict)
phd = get_per_hosts_dict(opts_dict)
final_dict: Dict[str, Any] = {}
for hostname_pat in sorted(phd, key=first_seen_positions.__getitem__):
if host_matches(hostname_pat, hostname, username):
od = phd[hostname_pat]
for k, v in od.items():
if isinstance(v, dict):
bv = final_dict.setdefault(k, {})
bv.update(v)
else:
final_dict[k] = v
first_seen_positions.clear()
return SSHOptions(final_dict)
def init_config(hostname: str, username: str, overrides: Optional[Iterable[str]] = None) -> SSHOptions:
config = tuple(resolve_config(SYSTEM_CONF, defconf))
return load_config(*config, overrides=overrides, hostname=hostname, username=username)

119
kittens/ssh/copy.py Normal file
View File

@@ -0,0 +1,119 @@
#!/usr/bin/env python
# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>
import glob
import os
import shlex
import uuid
from typing import (
Dict, Iterable, Iterator, List, NamedTuple, Optional, Sequence, Tuple
)
from kitty.cli import parse_args
from kitty.cli_stub import CopyCLIOptions
from kitty.types import run_once
from ..transfer.utils import expand_home, home_path
@run_once
def option_text() -> str:
return '''
--glob
type=bool-set
Interpret file arguments as glob patterns.
--dest
The destination on the remote host to copy to. Relative paths are resolved
relative to HOME on the remote host. When this option is not specified, the
local file path is used as the remote destination (with the HOME directory
getting automatically replaced by the remote HOME). Note that environment
variables and ~ are not expanded.
--exclude
type=list
A glob pattern. Files with names matching this pattern are excluded from being
transferred. Useful when adding directories. Can
be specified multiple times, if any of the patterns match the file will be
excluded. To exclude a directory use a pattern like */directory_name/*.
--symlink-strategy
default=preserve
choices=preserve,resolve,keep-path
Control what happens if the specified path is a symlink. The default is to preserve
the symlink, re-creating it on the remote machine. Setting this to :code:`resolve`
will cause the symlink to be followed and its target used as the file/directory to copy.
The value of :code:`keep-path` is the same as :code:`resolve` except that the remote
file path is derived from the symlink's path instead of the path of the symlink's target.
Note that this option does not apply to symlinks encountered while recursively copying directories.
'''
def parse_copy_args(args: Optional[Sequence[str]] = None) -> Tuple[CopyCLIOptions, List[str]]:
args = list(args or ())
try:
opts, args = parse_args(result_class=CopyCLIOptions, args=args, ospec=option_text)
except SystemExit as e:
raise CopyCLIError from e
return opts, args
def resolve_file_spec(spec: str, is_glob: bool) -> Iterator[str]:
ans = os.path.expandvars(expand_home(spec))
if not os.path.isabs(ans):
ans = expand_home(f'~/{ans}')
if is_glob:
files = glob.glob(ans)
if not files:
raise CopyCLIError(f'{spec} does not exist')
else:
if not os.path.exists(ans):
raise CopyCLIError(f'{spec} does not exist')
files = [ans]
for x in files:
yield os.path.normpath(x).replace(os.sep, '/')
class CopyCLIError(ValueError):
pass
def get_arcname(loc: str, dest: Optional[str], home: str) -> str:
if dest:
arcname = dest
else:
arcname = os.path.normpath(loc)
if arcname.startswith(home):
arcname = os.path.relpath(arcname, home)
arcname = os.path.normpath(arcname).replace(os.sep, '/')
prefix = 'root' if arcname.startswith('/') else 'home/'
return prefix + arcname
class CopyInstruction(NamedTuple):
local_path: str
arcname: str
exclude_patterns: Tuple[str, ...]
def parse_copy_instructions(val: str, current_val: Dict[str, str]) -> Iterable[Tuple[str, CopyInstruction]]:
opts, args = parse_copy_args(shlex.split(val))
locations: List[str] = []
for a in args:
locations.extend(resolve_file_spec(a, opts.glob))
if not locations:
raise CopyCLIError('No files to copy specified')
if len(locations) > 1 and opts.dest:
raise CopyCLIError('Specifying a remote location with more than one file is not supported')
home = home_path()
for loc in locations:
if opts.symlink_strategy != 'preserve':
rp = os.path.realpath(loc)
else:
rp = loc
arcname = get_arcname(rp if opts.symlink_strategy == 'resolve' else loc, opts.dest, home)
yield str(uuid.uuid4()), CopyInstruction(rp, arcname, tuple(opts.exclude))

View File

@@ -1,139 +1,303 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import fnmatch
import glob
import io
import json
import os import os
import re
import secrets
import shlex import shlex
import shutil
import stat
import subprocess import subprocess
import sys import sys
from contextlib import suppress import tarfile
from typing import List, NoReturn, Optional, Set, Tuple import tempfile
from .completion import ssh_options, complete import termios
import time
import traceback
from base64 import standard_b64decode, standard_b64encode
from contextlib import contextmanager, suppress
from getpass import getuser
from select import select
from typing import (
Any, Callable, Dict, Iterator, List, NoReturn, Optional, Sequence, Set,
Tuple, Union, cast
)
from kitty.utils import SSHConnectionData from kitty.constants import (
cache_dir, runtime_dir, shell_integration_dir, ssh_control_master_template,
str_version, terminfo_dir
)
from kitty.shell_integration import as_str_literal
from kitty.shm import SharedMemory
from kitty.types import run_once
from kitty.utils import (
SSHConnectionData, expandvars, resolve_abs_or_config_path,
set_echo as turn_off_echo
)
SHELL_SCRIPT = '''\ from ..tui.operations import (
#!/bin/sh RESTORE_PRIVATE_MODE_VALUES, SAVE_PRIVATE_MODE_VALUES, Mode,
# macOS ships with an ancient version of tic that cannot read from stdin, so we restore_colors, save_colors, set_mode
# create a temp file for it )
tmp=$(mktemp) from ..tui.utils import kitty_opts, running_in_tmux
cat >$tmp << 'TERMEOF' from .config import init_config
TERMINFO from .copy import CopyInstruction
TERMEOF from .options.types import Options as SSHOptions
from .options.utils import DELETE_ENV_VAR
tname=.terminfo from .utils import create_shared_memory, ssh_options
if [ -e "/usr/share/misc/terminfo.cdb" ]; then
# NetBSD requires this see https://github.com/kovidgoyal/kitty/issues/4622
tname=".terminfo.cdb"
fi
tic_out=$(tic -x -o $HOME/$tname $tmp 2>&1)
rc=$?
rm $tmp
if [ "$rc" != "0" ]; then echo "$tic_out"; exit 1; fi
if [ -z "$USER" ]; then export USER=$(whoami); fi
export TERMINFO="$HOME/$tname"
login_shell=""
python=""
login_shell_is_ok() {
if [ -z "$login_shell" ] || [ ! -x "$login_shell" ]; then return 1; fi
case "$login_shell" in
*sh) return 0;
esac
return 1;
}
detect_python() {
python=$(command -v python3)
if [ -z "$python" ]; then python=$(command -v python2); fi
if [ -z "$python" ]; then python=python; fi
}
using_getent() {
cmd=$(command -v getent)
if [ -z "$cmd" ]; then return; fi
output=$($cmd passwd $USER 2>/dev/null)
if [ $? = 0 ]; then login_shell=$(echo $output | cut -d: -f7); fi
}
using_id() {
cmd=$(command -v id)
if [ -z "$cmd" ]; then return; fi
output=$($cmd -P $USER 2>/dev/null)
if [ $? = 0 ]; then login_shell=$(echo $output | cut -d: -f7); fi
}
using_passwd() {
cmd=$(command -v grep)
if [ -z "$cmd" ]; then return; fi
output=$($cmd "^$USER:" /etc/passwd 2>/dev/null)
if [ $? = 0 ]; then login_shell=$(echo $output | cut -d: -f7); fi
}
using_python() {
detect_python
if [ ! -x "$python" ]; then return; fi
output=$($python -c "import pwd, os; print(pwd.getpwuid(os.geteuid()).pw_shell)")
if [ $? = 0 ]; then login_shell=$output; fi
}
execute_with_python() {
detect_python
exec $python -c "import os; os.execl('$login_shell', '-' '$shell_name')"
}
die() { echo "$*" 1>&2 ; exit 1; }
using_getent
if ! login_shell_is_ok; then using_id; fi
if ! login_shell_is_ok; then using_python; fi
if ! login_shell_is_ok; then using_passwd; fi
if ! login_shell_is_ok; then die "Could not detect login shell"; fi
# If a command was passed to SSH execute it here @run_once
EXEC_CMD def ssh_exe() -> str:
return shutil.which('ssh') or 'ssh'
# We need to pass the first argument to the executed program with a leading -
# to make sure the shell executes as a login shell. Note that not all shells
# support exec -a so we use the below to try to detect such shells
shell_name=$(basename $login_shell)
if [ -z "$PIPESTATUS" ]; then
# the dash shell does not support exec -a and also does not define PIPESTATUS
execute_with_python
fi
exec -a "-$shell_name" $login_shell
'''
PYTHON_SCRIPT = '''\ def read_data_from_shared_memory(shm_name: str) -> Any:
#!/usr/bin/env python with SharedMemory(shm_name, readonly=True) as shm:
from __future__ import print_function shm.unlink()
from tempfile import NamedTemporaryFile if shm.stats.st_uid != os.geteuid() or shm.stats.st_gid != os.getegid():
import subprocess, os, sys, pwd, binascii, json raise ValueError('Incorrect owner on pwfile')
mode = stat.S_IMODE(shm.stats.st_mode)
if mode != stat.S_IREAD:
raise ValueError('Incorrect permissions on pwfile')
return json.loads(shm.read_data_with_size())
# macOS ships with an ancient version of tic that cannot read from stdin, so we
# create a temp file for it # See https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html
with NamedTemporaryFile() as tmp: quote_pat = re.compile('([\\`"])')
tname = '.terminfo'
if os.path.exists('/usr/share/misc/terminfo.cdb'):
tname += '.cdb' def quote_env_val(x: str, literal_quote: bool = False) -> str:
tmp.write(binascii.unhexlify('{terminfo}')) if literal_quote:
tmp.flush() return as_str_literal(x)
p = subprocess.Popen(['tic', '-x', '-o', os.path.expanduser('~/' + tname), tmp.name], stdout=subprocess.PIPE, stderr=subprocess.PIPE) x = quote_pat.sub(r'\\\1', x)
stdout, stderr = p.communicate() x = x.replace('$(', r'\$(') # prevent execution with $()
if p.wait() != 0: return f'"{x}"'
getattr(sys.stderr, 'buffer', sys.stderr).write(stdout + stderr)
raise SystemExit('Failed to compile terminfo using tic')
command_to_execute = json.loads(binascii.unhexlify('{command_to_execute}')) def serialize_env(literal_env: Dict[str, str], env: Dict[str, str], base_env: Dict[str, str], for_python: bool = False) -> bytes:
try: lines = []
shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh' literal_quote = True
except KeyError:
shell_path = '/bin/sh' if for_python:
shell_name = '-' + os.path.basename(shell_path) def a(k: str, val: str = '', prefix: str = 'export') -> None:
if command_to_execute: if val:
os.execlp(shell_path, shell_path, '-c', command_to_execute) lines.append(f'{prefix} {json.dumps((k, val, literal_quote))}')
os.execlp(shell_path, shell_name) else:
''' lines.append(f'{prefix} {json.dumps((k,))}')
else:
def a(k: str, val: str = '', prefix: str = 'export') -> None:
if val:
lines.append(f'{prefix} {shlex.quote(k)}={quote_env_val(val, literal_quote)}')
else:
lines.append(f'{prefix} {shlex.quote(k)}')
for k, v in literal_env.items():
a(k, v)
literal_quote = False
for k in sorted(env):
v = env[k]
if v == DELETE_ENV_VAR:
a(k, prefix='unset')
elif v == '_kitty_copy_env_var_':
q = base_env.get(k)
if q is not None:
a(k, q)
else:
a(k, v)
return '\n'.join(lines).encode('utf-8')
def make_tarfile(ssh_opts: SSHOptions, base_env: Dict[str, str], compression: str = 'gz', literal_env: Dict[str, str] = {}) -> bytes:
def normalize_tarinfo(tarinfo: tarfile.TarInfo) -> tarfile.TarInfo:
tarinfo.uname = tarinfo.gname = ''
tarinfo.uid = tarinfo.gid = 0
# some distro's like nix mess with installed file permissions so ensure
# files are at least readable and writable by owning user
tarinfo.mode |= stat.S_IWUSR | stat.S_IRUSR
return tarinfo
def add_data_as_file(tf: tarfile.TarFile, arcname: str, data: Union[str, bytes]) -> tarfile.TarInfo:
ans = tarfile.TarInfo(arcname)
ans.mtime = 0
ans.type = tarfile.REGTYPE
if isinstance(data, str):
data = data.encode('utf-8')
ans.size = len(data)
normalize_tarinfo(ans)
tf.addfile(ans, io.BytesIO(data))
return ans
def filter_from_globs(*pats: str) -> Callable[[tarfile.TarInfo], Optional[tarfile.TarInfo]]:
def filter(tarinfo: tarfile.TarInfo) -> Optional[tarfile.TarInfo]:
for junk_dir in ('.DS_Store', '__pycache__'):
for pat in (f'*/{junk_dir}', f'*/{junk_dir}/*'):
if fnmatch.fnmatch(tarinfo.name, pat):
return None
for pat in pats:
if fnmatch.fnmatch(tarinfo.name, pat):
return None
return normalize_tarinfo(tarinfo)
return filter
from kitty.shell_integration import get_effective_ksi_env_var
if ssh_opts.shell_integration == 'inherited':
ksi = get_effective_ksi_env_var(kitty_opts())
else:
from kitty.options.types import Options
from kitty.options.utils import shell_integration
ksi = get_effective_ksi_env_var(Options({'shell_integration': shell_integration(ssh_opts.shell_integration)}))
env = {
'TERM': os.environ.get('TERM') or kitty_opts().term,
'COLORTERM': 'truecolor',
}
env.update(ssh_opts.env)
for q in ('KITTY_WINDOW_ID', 'WINDOWID'):
val = os.environ.get(q)
if val is not None:
env[q] = val
env['KITTY_SHELL_INTEGRATION'] = ksi or DELETE_ENV_VAR
env['KITTY_SSH_KITTEN_DATA_DIR'] = ssh_opts.remote_dir
if ssh_opts.login_shell:
env['KITTY_LOGIN_SHELL'] = ssh_opts.login_shell
if ssh_opts.cwd:
env['KITTY_LOGIN_CWD'] = ssh_opts.cwd
if ssh_opts.remote_kitty != 'no':
env['KITTY_REMOTE'] = ssh_opts.remote_kitty
if os.environ.get('KITTY_PUBLIC_KEY'):
env['KITTY_PUBLIC_KEY'] = os.environ['KITTY_PUBLIC_KEY']
env_script = serialize_env(literal_env, env, base_env, for_python=compression != 'gz')
buf = io.BytesIO()
with tarfile.open(mode=f'w:{compression}', fileobj=buf, encoding='utf-8') as tf:
rd = ssh_opts.remote_dir.rstrip('/')
for ci in ssh_opts.copy.values():
tf.add(ci.local_path, arcname=ci.arcname, filter=filter_from_globs(*ci.exclude_patterns))
add_data_as_file(tf, 'data.sh', env_script)
if compression == 'gz':
tf.add(f'{shell_integration_dir}/ssh/bootstrap-utils.sh', arcname='bootstrap-utils.sh', filter=normalize_tarinfo)
if ksi:
arcname = 'home/' + rd + '/shell-integration'
tf.add(shell_integration_dir, arcname=arcname, filter=filter_from_globs(
f'{arcname}/ssh/*', # bootstrap files are sent as command line args
f'{arcname}/zsh/kitty.zsh', # present for legacy compat not needed by ssh kitten
))
if ssh_opts.remote_kitty != 'no':
arcname = 'home/' + rd + '/kitty'
add_data_as_file(tf, arcname + '/version', str_version.encode('ascii'))
tf.add(shell_integration_dir + '/ssh/kitty', arcname=arcname + '/bin/kitty', filter=normalize_tarinfo)
tf.add(f'{terminfo_dir}/kitty.terminfo', arcname='home/.terminfo/kitty.terminfo', filter=normalize_tarinfo)
tf.add(glob.glob(f'{terminfo_dir}/*/xterm-kitty')[0], arcname='home/.terminfo/x/xterm-kitty', filter=normalize_tarinfo)
return buf.getvalue()
def get_ssh_data(msg: str, request_id: str) -> Iterator[bytes]:
yield b'\nKITTY_DATA_START\n' # to discard leading data
try:
msg = standard_b64decode(msg).decode('utf-8')
md = dict(x.split('=', 1) for x in msg.split(':'))
pw = md['pw']
pwfilename = md['pwfile']
rq_id = md['id']
except Exception:
traceback.print_exc()
yield b'invalid ssh data request message\n'
else:
try:
env_data = read_data_from_shared_memory(pwfilename)
if pw != env_data['pw']:
raise ValueError('Incorrect password')
if rq_id != request_id:
raise ValueError(f'Incorrect request id: {rq_id!r} expecting the KITTY_PID-KITTY_WINDOW_ID for the current kitty window')
except Exception as e:
traceback.print_exc()
yield f'{e}\n'.encode('utf-8')
else:
yield b'OK\n'
ssh_opts = SSHOptions(env_data['opts'])
ssh_opts.copy = {k: CopyInstruction(*v) for k, v in ssh_opts.copy.items()}
encoded_data = memoryview(env_data['tarfile'].encode('ascii'))
# macOS has a 255 byte limit on its input queue as per man stty.
# Not clear if that applies to canonical mode input as well, but
# better to be safe.
line_sz = 254
while encoded_data:
yield encoded_data[:line_sz]
yield b'\n'
encoded_data = encoded_data[line_sz:]
yield b'KITTY_DATA_END\n'
def safe_remove(x: str) -> None:
with suppress(OSError):
os.remove(x)
def prepare_script(ans: str, replacements: Dict[str, str], script_type: str) -> str:
for k in ('EXEC_CMD', 'EXPORT_HOME_CMD'):
replacements[k] = replacements.get(k, '')
def sub(m: 're.Match[str]') -> str:
return replacements[m.group()]
return re.sub('|'.join(fr'\b{k}\b' for k in replacements), sub, ans)
def prepare_exec_cmd(remote_args: Sequence[str], is_python: bool) -> str:
# ssh simply concatenates multiple commands using a space see
# line 1129 of ssh.c and on the remote side sshd.c runs the
# concatenated command as shell -c cmd
if is_python:
return standard_b64encode(' '.join(remote_args).encode('utf-8')).decode('ascii')
args = ' '.join(c.replace("'", """'"'"'""") for c in remote_args)
return f"""unset KITTY_SHELL_INTEGRATION; exec "$login_shell" -c '{args}'"""
def prepare_export_home_cmd(ssh_opts: SSHOptions, is_python: bool) -> str:
home = ssh_opts.env.get('HOME')
if home == '_kitty_copy_env_var_':
home = os.environ.get('HOME')
if home:
if is_python:
return standard_b64encode(home.encode('utf-8')).decode('ascii')
else:
return f'export HOME={quote_env_val(home)}; cd "$HOME"'
return ''
def bootstrap_script(
ssh_opts: SSHOptions, script_type: str = 'sh', remote_args: Sequence[str] = (),
test_script: str = '', request_id: Optional[str] = None, cli_hostname: str = '', cli_uname: str = '',
request_data: bool = False, echo_on: bool = True, literal_env: Dict[str, str] = {}
) -> Tuple[str, Dict[str, str], str]:
if request_id is None:
request_id = os.environ['KITTY_PID'] + '-' + os.environ['KITTY_WINDOW_ID']
is_python = script_type == 'py'
export_home_cmd = prepare_export_home_cmd(ssh_opts, is_python) if 'HOME' in ssh_opts.env else ''
exec_cmd = prepare_exec_cmd(remote_args, is_python) if remote_args else ''
with open(os.path.join(shell_integration_dir, 'ssh', f'bootstrap.{script_type}')) as f:
ans = f.read()
pw = secrets.token_hex()
tfd = standard_b64encode(make_tarfile(ssh_opts, dict(os.environ), 'gz' if script_type == 'sh' else 'bz2', literal_env=literal_env)).decode('ascii')
data = {'pw': pw, 'opts': ssh_opts._asdict(), 'hostname': cli_hostname, 'uname': cli_uname, 'tarfile': tfd}
shm_name = create_shared_memory(data, prefix=f'kssh-{os.getpid()}-')
sensitive_data = {'REQUEST_ID': request_id, 'DATA_PASSWORD': pw, 'PASSWORD_FILENAME': shm_name}
replacements = {
'EXPORT_HOME_CMD': export_home_cmd,
'EXEC_CMD': exec_cmd, 'TEST_SCRIPT': test_script,
'REQUEST_DATA': '1' if request_data else '0', 'ECHO_ON': '1' if echo_on else '0',
}
sd = replacements.copy()
if request_data:
sd.update(sensitive_data)
replacements.update(sensitive_data)
return prepare_script(ans, sd, script_type), replacements, shm_name
def get_ssh_cli() -> Tuple[Set[str], Set[str]]: def get_ssh_cli() -> Tuple[Set[str], Set[str]]:
@@ -148,13 +312,22 @@ def get_ssh_cli() -> Tuple[Set[str], Set[str]]:
return boolean_ssh_args, other_ssh_args return boolean_ssh_args, other_ssh_args
def get_connection_data(args: List[str], cwd: str = '') -> Optional[SSHConnectionData]: def is_extra_arg(arg: str, extra_args: Tuple[str, ...]) -> str:
for x in extra_args:
if arg == x or arg.startswith(f'{x}='):
return x
return ''
def get_connection_data(args: List[str], cwd: str = '', extra_args: Tuple[str, ...] = ()) -> Optional[SSHConnectionData]:
boolean_ssh_args, other_ssh_args = get_ssh_cli() boolean_ssh_args, other_ssh_args = get_ssh_cli()
port: Optional[int] = None port: Optional[int] = None
expecting_port = expecting_identity = False expecting_port = expecting_identity = False
expecting_option_val = False expecting_option_val = False
expecting_hostname = False expecting_hostname = False
expecting_extra_val = ''
host_name = identity_file = found_ssh = '' host_name = identity_file = found_ssh = ''
found_extra_args: List[Tuple[str, str]] = []
for i, arg in enumerate(args): for i, arg in enumerate(args):
if not found_ssh: if not found_ssh:
@@ -182,6 +355,15 @@ def get_connection_data(args: List[str], cwd: str = '') -> Optional[SSHConnectio
else: else:
identity_file = arg[2:] identity_file = arg[2:]
continue continue
if arg.startswith('--') and extra_args:
matching_ex = is_extra_arg(arg, extra_args)
if matching_ex:
if '=' in arg:
exval = arg.partition('=')[-1]
found_extra_args.append((matching_ex, exval))
continue
expecting_extra_val = matching_ex
expecting_option_val = True expecting_option_val = True
continue continue
@@ -192,6 +374,9 @@ def get_connection_data(args: List[str], cwd: str = '') -> Optional[SSHConnectio
expecting_port = False expecting_port = False
elif expecting_identity: elif expecting_identity:
identity_file = arg identity_file = arg
elif expecting_extra_val:
found_extra_args.append((expecting_extra_val, arg))
expecting_extra_val = ''
expecting_option_val = False expecting_option_val = False
continue continue
@@ -199,13 +384,22 @@ def get_connection_data(args: List[str], cwd: str = '') -> Optional[SSHConnectio
host_name = arg host_name = arg
if not host_name: if not host_name:
return None return None
if host_name.startswith('ssh://'):
from urllib.parse import urlparse
purl = urlparse(host_name)
if purl.hostname:
host_name = purl.hostname
if purl.username:
host_name = f'{purl.username}@{host_name}'
if port is None and purl.port:
port = purl.port
if identity_file: if identity_file:
if not os.path.isabs(identity_file): if not os.path.isabs(identity_file):
identity_file = os.path.expanduser(identity_file) identity_file = os.path.expanduser(identity_file)
if not os.path.isabs(identity_file): if not os.path.isabs(identity_file):
identity_file = os.path.normpath(os.path.join(cwd or os.getcwd(), identity_file)) identity_file = os.path.normpath(os.path.join(cwd or os.getcwd(), identity_file))
return SSHConnectionData(found_ssh, host_name, port, identity_file) return SSHConnectionData(found_ssh, host_name, port, identity_file, tuple(found_extra_args))
class InvalidSSHArgs(ValueError): class InvalidSSHArgs(ValueError):
@@ -217,17 +411,21 @@ class InvalidSSHArgs(ValueError):
def system_exit(self) -> None: def system_exit(self) -> None:
if self.err_msg: if self.err_msg:
print(self.err_msg, file=sys.stderr) print(self.err_msg, file=sys.stderr)
os.execlp('ssh', 'ssh') os.execlp(ssh_exe(), 'ssh')
def parse_ssh_args(args: List[str]) -> Tuple[List[str], List[str], bool]: passthrough_args = {f'-{x}' for x in 'NnfGT'}
def parse_ssh_args(args: List[str], extra_args: Tuple[str, ...] = ()) -> Tuple[List[str], List[str], bool, Tuple[str, ...]]:
boolean_ssh_args, other_ssh_args = get_ssh_cli() boolean_ssh_args, other_ssh_args = get_ssh_cli()
passthrough_args = {f'-{x}' for x in 'Nnf'}
ssh_args = [] ssh_args = []
server_args: List[str] = [] server_args: List[str] = []
expecting_option_val = False expecting_option_val = False
passthrough = False passthrough = False
stop_option_processing = False stop_option_processing = False
found_extra_args: List[str] = []
expecting_extra_val = ''
for argument in args: for argument in args:
if len(server_args) > 1 or stop_option_processing: if len(server_args) > 1 or stop_option_processing:
server_args.append(argument) server_args.append(argument)
@@ -236,6 +434,16 @@ def parse_ssh_args(args: List[str]) -> Tuple[List[str], List[str], bool]:
if argument == '--': if argument == '--':
stop_option_processing = True stop_option_processing = True
continue continue
if extra_args:
matching_ex = is_extra_arg(argument, extra_args)
if matching_ex:
if '=' in argument:
exval = argument.partition('=')[-1]
found_extra_args.extend((matching_ex, exval))
else:
expecting_extra_val = matching_ex
expecting_option_val = True
continue
# could be a multi-character option # could be a multi-character option
all_args = argument[1:] all_args = argument[1:]
for i, arg in enumerate(all_args): for i, arg in enumerate(all_args):
@@ -256,75 +464,298 @@ def parse_ssh_args(args: List[str]) -> Tuple[List[str], List[str], bool]:
raise InvalidSSHArgs(f'unknown option -- {arg[1:]}') raise InvalidSSHArgs(f'unknown option -- {arg[1:]}')
continue continue
if expecting_option_val: if expecting_option_val:
ssh_args.append(argument) if expecting_extra_val:
found_extra_args.extend((expecting_extra_val, argument))
expecting_extra_val = ''
else:
ssh_args.append(argument)
expecting_option_val = False expecting_option_val = False
continue continue
server_args.append(argument) server_args.append(argument)
if not server_args: if not server_args:
raise InvalidSSHArgs() raise InvalidSSHArgs()
return ssh_args, server_args, passthrough return ssh_args, server_args, passthrough, tuple(found_extra_args)
def quote(x: str) -> str: def wrap_bootstrap_script(sh_script: str, interpreter: str) -> List[str]:
# we have to escape unbalanced quotes and other unparsable # sshd will execute the command we pass it by join all command line
# args as they will break the shell script # arguments with a space and passing it as a single argument to the users
# But we do not want to quote things like * or 'echo hello' # login shell with -c. If the user has a non POSIX login shell it might
# See https://github.com/kovidgoyal/kitty/issues/1787 # have different escaping semantics and syntax, so the command it should
try: # execute has to be as simple as possible, basically of the form
shlex.split(x) # interpreter -c unwrap_script escaped_bootstrap_script
except ValueError: # The unwrap_script is responsible for unescaping the bootstrap script and
x = shlex.quote(x) # executing it.
return x q = os.path.basename(interpreter).lower()
is_python = 'python' in q
if is_python:
es = standard_b64encode(sh_script.encode('utf-8')).decode('ascii')
unwrap_script = '''"import base64, sys; eval(compile(base64.standard_b64decode(sys.argv[-1]), 'bootstrap.py', 'exec'))"'''
else:
# We cant rely on base64 being available on the remote system, so instead
# we quote the bootstrap script by replacing ' and \ with \v and \f
# also replacing \n and ! with \r and \b for tcsh
# finally surrounding with '
es = "'" + sh_script.replace("'", '\v').replace('\\', '\f').replace('\n', '\r').replace('!', '\b') + "'"
unwrap_script = r"""'eval "$(echo "$0" | tr \\\v\\\f\\\r\\\b \\\047\\\134\\\n\\\041)"' """
# exec is supported by all sh like shells, and fish and csh
return ['exec', interpreter, '-c', unwrap_script, es]
def get_posix_cmd(terminfo: str, remote_args: List[str]) -> List[str]: def get_remote_command(
sh_script = SHELL_SCRIPT.replace('TERMINFO', terminfo, 1) remote_args: List[str], ssh_opts: SSHOptions, cli_hostname: str = '', cli_uname: str = '',
command_to_execute = '' echo_on: bool = True, request_data: bool = False, literal_env: Dict[str, str] = {}
if remote_args: ) -> Tuple[List[str], Dict[str, str], str]:
# ssh simply concatenates multiple commands using a space see interpreter = ssh_opts.interpreter
# line 1129 of ssh.c and on the remote side sshd.c runs the q = os.path.basename(interpreter).lower()
# concatenated command as shell -c cmd is_python = 'python' in q
args = [c.replace("'", """'"'"'""") for c in remote_args] sh_script, replacements, shm_name = bootstrap_script(
command_to_execute = "exec $login_shell -c '{}'".format(' '.join(args)) ssh_opts, script_type='py' if is_python else 'sh', remote_args=remote_args, literal_env=literal_env,
sh_script = sh_script.replace('EXEC_CMD', command_to_execute) cli_hostname=cli_hostname, cli_uname=cli_uname, echo_on=echo_on, request_data=request_data)
return [f'sh -c {shlex.quote(sh_script)}'] return wrap_bootstrap_script(sh_script, interpreter), replacements, shm_name
def get_python_cmd(terminfo: str, command_to_execute: List[str]) -> List[str]: def connection_sharing_args(kitty_pid: int) -> List[str]:
import json rd = runtime_dir()
script = PYTHON_SCRIPT.format( # Bloody OpenSSH generates a 40 char hash and in creating the socket
terminfo=terminfo.encode('utf-8').hex(), # appends a 27 char temp suffix to it. Socket max path length is approx
command_to_execute=json.dumps(' '.join(command_to_execute)).encode('utf-8').hex() # ~104 chars. macOS has no system runtime dir so we use a cache dir in
# /Users/WHY_DOES_ANYONE_USE_MACOS/Library/Caches/APPLE_ARE_IDIOTIC
if len(rd) > 35 and os.path.isdir('/tmp'):
idiotic_design = f'/tmp/kssh-rdir-{os.getuid()}'
try:
os.symlink(rd, idiotic_design)
except FileExistsError:
try:
dest = os.readlink(idiotic_design)
except OSError as e:
raise ValueError(f'The {idiotic_design} symlink could not be created as something with that name exists already') from e
else:
if dest != rd:
with tempfile.TemporaryDirectory(dir='/tmp') as tdir:
tlink = os.path.join(tdir, 'sigh')
os.symlink(rd, tlink)
os.rename(tlink, idiotic_design)
rd = idiotic_design
cp = os.path.join(rd, ssh_control_master_template.format(kitty_pid=kitty_pid, ssh_placeholder='%C'))
ans: List[str] = [
'-o', 'ControlMaster=auto',
'-o', f'ControlPath={cp}',
'-o', 'ControlPersist=yes',
'-o', 'ServerAliveInterval=60',
'-o', 'ServerAliveCountMax=5',
'-o', 'TCPKeepAlive=no',
]
return ans
@contextmanager
def restore_terminal_state() -> Iterator[bool]:
with open(os.ctermid()) as f:
val = termios.tcgetattr(f.fileno())
print(end=SAVE_PRIVATE_MODE_VALUES)
print(end=set_mode(Mode.HANDLE_TERMIOS_SIGNALS), flush=True)
try:
yield bool(val[3] & termios.ECHO)
finally:
termios.tcsetattr(f.fileno(), termios.TCSAFLUSH, val)
print(end=RESTORE_PRIVATE_MODE_VALUES, flush=True)
def dcs_to_kitty(payload: Union[bytes, str], type: str = 'ssh') -> bytes:
if isinstance(payload, str):
payload = payload.encode('utf-8')
payload = standard_b64encode(payload)
ans = b'\033P@kitty-' + type.encode('ascii') + b'|' + payload
tmux = running_in_tmux()
if tmux:
cp = subprocess.run([tmux, 'set', '-p', 'allow-passthrough', 'on'])
if cp.returncode != 0:
raise SystemExit(cp.returncode)
ans = b'\033Ptmux;\033' + ans + b'\033\033\\\033\\'
else:
ans += b'\033\\'
return ans
@run_once
def ssh_version() -> Tuple[int, int]:
o = subprocess.check_output([ssh_exe(), '-V'], stderr=subprocess.STDOUT).decode()
m = re.match(r'OpenSSH_(\d+).(\d+)', o)
if m is None:
raise ValueError(f'Invalid version string for OpenSSH: {o}')
return int(m.group(1)), int(m.group(2))
@contextmanager
def drain_potential_tty_garbage(p: 'subprocess.Popen[bytes]', data_request: str) -> Iterator[None]:
with open(os.open(os.ctermid(), os.O_CLOEXEC | os.O_RDWR | os.O_NOCTTY), 'wb') as tty:
if data_request:
turn_off_echo(tty.fileno())
tty.write(dcs_to_kitty(data_request))
tty.flush()
try:
yield
finally:
# discard queued input data on tty in case data transmission was
# interrupted due to SSH failure, avoids spewing garbage to screen
from uuid import uuid4
canary = uuid4().hex.encode('ascii')
turn_off_echo(tty.fileno())
tty.write(dcs_to_kitty(canary + b'\n\r', type='echo'))
tty.flush()
data = b''
give_up_at = time.monotonic() + 2
tty_fd = tty.fileno()
while time.monotonic() < give_up_at and canary not in data:
with suppress(KeyboardInterrupt):
rd, wr, err = select([tty_fd], [], [tty_fd], max(0, give_up_at - time.monotonic()))
if err or not rd:
break
q = os.read(tty_fd, io.DEFAULT_BUFFER_SIZE)
if not q:
break
data += q
def change_colors(color_scheme: str) -> bool:
if not color_scheme:
return False
from kittens.themes.collection import (
NoCacheFound, load_themes, text_as_opts
) )
return [f'python -c "{script}"'] from kittens.themes.main import colors_as_escape_codes
if color_scheme.endswith('.conf'):
conf_file = resolve_abs_or_config_path(color_scheme)
try:
with open(conf_file) as f:
opts = text_as_opts(f.read())
except FileNotFoundError:
raise SystemExit(f'Failed to find the color conf file: {expandvars(conf_file)}')
else:
try:
themes = load_themes(-1)
except NoCacheFound:
themes = load_themes()
cs = expandvars(color_scheme)
try:
theme = themes[cs]
except KeyError:
raise SystemExit(f'Failed to find the color theme: {cs}')
opts = theme.kitty_opts
raw = colors_as_escape_codes(opts)
print(save_colors(), sep='', end=raw, flush=True)
return True
def main(args: List[str]) -> NoReturn: def add_cloned_env(shm_name: str) -> Dict[str, str]:
args = args[1:]
use_posix = True
if args and args[0] == 'use-python':
args = args[1:]
use_posix = False
try: try:
ssh_args, server_args, passthrough = parse_ssh_args(args) return cast(Dict[str, str], read_data_from_shared_memory(shm_name))
except FileNotFoundError:
pass
return {}
def run_ssh(ssh_args: List[str], server_args: List[str], found_extra_args: Tuple[str, ...]) -> NoReturn:
cmd = [ssh_exe()] + ssh_args
hostname, remote_args = server_args[0], server_args[1:]
if not remote_args:
cmd.append('-t')
insertion_point = len(cmd)
cmd.append('--')
cmd.append(hostname)
uname = getuser()
if hostname.startswith('ssh://'):
from urllib.parse import urlparse
purl = urlparse(hostname)
hostname_for_match = purl.hostname or hostname[6:].split('/', 1)[0]
uname = purl.username or uname
elif '@' in hostname and hostname[0] != '@':
uname, hostname_for_match = hostname.split('@', 1)
else:
hostname_for_match = hostname
hostname_for_match = hostname_for_match.split('@', 1)[-1].split(':', 1)[0]
overrides: List[str] = []
literal_env: Dict[str, str] = {}
pat = re.compile(r'^([a-zA-Z0-9_]+)[ \t]*=')
for i, a in enumerate(found_extra_args):
if i % 2 == 1:
aq = pat.sub(r'\1 ', a.lstrip())
key = aq.split(maxsplit=1)[0]
if key == 'clone_env':
literal_env = add_cloned_env(aq.split(maxsplit=1)[1])
elif key != 'hostname':
overrides.append(aq)
if overrides:
overrides.insert(0, f'hostname {uname}@{hostname_for_match}')
host_opts = init_config(hostname_for_match, uname, overrides)
if host_opts.share_connections:
cmd[insertion_point:insertion_point] = connection_sharing_args(int(os.environ['KITTY_PID']))
use_kitty_askpass = host_opts.askpass == 'native' or (host_opts.askpass == 'unless-set' and 'SSH_ASKPASS' not in os.environ)
need_to_request_data = True
if use_kitty_askpass:
sentinel = os.path.join(cache_dir(), 'openssh-is-new-enough-for-askpass')
sentinel_exists = os.path.exists(sentinel)
if sentinel_exists or ssh_version() >= (8, 4):
if not sentinel_exists:
open(sentinel, 'w').close()
# SSH_ASKPASS_REQUIRE was introduced in 8.4 release on 2020-09-27
need_to_request_data = False
os.environ['SSH_ASKPASS_REQUIRE'] = 'force'
os.environ['SSH_ASKPASS'] = os.path.join(shell_integration_dir, 'ssh', 'askpass.py')
if need_to_request_data and host_opts.share_connections:
cp = subprocess.run(cmd[:1] + ['-O', 'check'] + cmd[1:], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
if cp.returncode == 0:
# we will use the master connection so SSH does not need to use the tty
need_to_request_data = False
with restore_terminal_state() as echo_on:
rcmd, replacements, shm_name = get_remote_command(
remote_args, host_opts, hostname_for_match, uname, echo_on, request_data=need_to_request_data, literal_env=literal_env)
cmd += rcmd
colors_changed = change_colors(host_opts.color_scheme)
try:
p = subprocess.Popen(cmd)
except FileNotFoundError:
raise SystemExit('Could not find the ssh executable, is it in your PATH?')
else:
rq = '' if need_to_request_data else 'id={REQUEST_ID}:pwfile={PASSWORD_FILENAME}:pw={DATA_PASSWORD}'.format(**replacements)
with drain_potential_tty_garbage(p, rq):
raise SystemExit(p.wait())
finally:
if colors_changed:
print(end=restore_colors(), flush=True)
def main(args: List[str]) -> None:
args = args[1:]
if args and args[0] == 'use-python':
args = args[1:] # backwards compat from when we had a python implementation
try:
ssh_args, server_args, passthrough, found_extra_args = parse_ssh_args(args, extra_args=('--kitten',))
except InvalidSSHArgs as e: except InvalidSSHArgs as e:
e.system_exit() e.system_exit()
cmd = ['ssh'] + ssh_args
if passthrough: if passthrough:
cmd += server_args if found_extra_args:
else: raise SystemExit(f'The SSH kitten cannot work with the options: {", ".join(passthrough_args)}')
hostname, remote_args = server_args[0], server_args[1:] os.execlp(ssh_exe(), 'ssh', *args)
if not remote_args:
cmd.append('-t') if not os.environ.get('KITTY_WINDOW_ID') or not os.environ.get('KITTY_PID'):
cmd.append('--') raise SystemExit('The SSH kitten is meant to run inside a kitty window')
cmd.append(hostname) if not sys.stdin.isatty():
terminfo = subprocess.check_output(['infocmp', '-a']).decode('utf-8') raise SystemExit('The SSH kitten is meant for interactive use only, STDIN must be a terminal')
f = get_posix_cmd if use_posix else get_python_cmd try:
cmd += f(terminfo, remote_args) run_ssh(ssh_args, server_args, found_extra_args)
os.execvp('ssh', cmd) except KeyboardInterrupt:
sys.excepthook = lambda *a: None
raise
if __name__ == '__main__': if __name__ == '__main__':
main(sys.argv) main(sys.argv)
elif __name__ == '__completer__': elif __name__ == '__completer__':
from .completion import complete
setattr(sys, 'kitten_completer', complete) setattr(sys, 'kitten_completer', complete)
elif __name__ == '__conf__':
from .options.definition import definition
sys.options_definition = definition # type: ignore

View File

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