Compare commits

...

274 Commits

Author SHA1 Message Date
Kovid Goyal
f66db65974 version 0.11.1 2018-06-17 10:57:55 +05:30
Kovid Goyal
5b6038db35 Dont include a literal dump of the conf files
Since all the config options are documented separately on the page in
any case, there is not much need for it. And now that the conf files are
downloadable, it is probably nicer to just view/edit them directly in
the users favorite pager/editor rather than the browser.
2018-06-16 22:31:21 +05:30
Kovid Goyal
9a365b0968 Merge branch 'docs' of https://github.com/blueyed/kitty 2018-06-16 22:26:45 +05:30
Daniel Hahler
8a0cd73821 docs: improve generation of default config files
- config files are placed into `docs/generated/conf/`, using the
  expected name, and without any indentation
- `literalinclude::` is used instead of `include::`
- uses `:download:` instead of `:file:` to allow for downloading the
  generated files easily
2018-06-16 18:35:58 +02:00
Kovid Goyal
49b065cbe7 Use conf_dir instead of cwd as base for session file 2018-06-16 10:22:46 +05:30
Kovid Goyal
77f651cb5d Expand env vars for the start_session config variable 2018-06-16 10:18:48 +05:30
Kovid Goyal
fedde51c12 ... 2018-06-16 10:16:06 +05:30
Kovid Goyal
f710d6906e Add a linkcheck target to check links in the docs 2018-06-16 09:27:39 +05:30
Kovid Goyal
c9a751360e Update CHANGELOG 2018-06-16 09:25:00 +05:30
Kovid Goyal
ad97315925 diff kitten: Misc fixes to msg display in the status line 2018-06-16 08:57:02 +05:30
Kovid Goyal
e8f7a2b345 Dont send on_text events with empty text 2018-06-16 08:55:10 +05:30
Kovid Goyal
5bc8d05f0a ... 2018-06-16 08:49:08 +05:30
Kovid Goyal
3669d4135c diff kitten: Add keybindings to search without regex 2018-06-16 08:45:42 +05:30
Kovid Goyal
73b870bb87 ... 2018-06-16 08:41:19 +05:30
Kovid Goyal
6b4217c12a Use a single build script on Travis
Allows the build to fail faster if any of the intermediate steps fails
2018-06-16 08:33:07 +05:30
Kovid Goyal
8b4fe494cf Format matches count the same as scroll percent 2018-06-16 08:24:32 +05:30
Kovid Goyal
35041e7709 Fix docs links 2018-06-16 08:19:33 +05:30
Kovid Goyal
12cbcf1c17 Add an option to kitty.conf to specify a default startup session
Fixes #641
2018-06-16 08:14:11 +05:30
Kovid Goyal
26f50a451e Add a section on configuration to the main page in the docs 2018-06-16 07:45:24 +05:30
Kovid Goyal
11f3d7e215 ... 2018-06-15 17:53:04 +05:30
Kovid Goyal
a7d533cfe5 Increase contrast of link color with text color in docs 2018-06-15 15:38:30 +05:30
Kovid Goyal
52ec4dc21b Allow cross-linking to @ command documentation 2018-06-15 15:31:05 +05:30
Kovid Goyal
6f7ea49008 Switch to high number OSC codes instead of DCS string codes for dynamic color push/pop 2018-06-15 15:21:46 +05:30
Kovid Goyal
84b99b1b3d diff kitten: Clear the search when pressing Esc 2018-06-15 15:00:09 +05:30
Kovid Goyal
1022d8a0ee diff kitten: Show the number of matches in the status bar when doing a search 2018-06-15 14:57:03 +05:30
Kovid Goyal
cd6a46730a ... 2018-06-15 14:48:59 +05:30
Kovid Goyal
59743fd7a1 diff kitten: Allow theming the selection background/foreground as well 2018-06-15 14:43:39 +05:30
Kovid Goyal
07bd371e21 Re-org diff kitten docs
Move the auto-generated bits to the end
2018-06-15 14:31:13 +05:30
Kovid Goyal
fe3b10a8fb diff kitten: Implement searching for text in the diff
Fixes #574
2018-06-15 14:28:42 +05:30
Kovid Goyal
c994bc1d89 Add more clarification about kitty's performance goals 2018-06-15 13:51:49 +05:30
Kovid Goyal
22cffef7c8 diff kitten: Use a single-width replacement for control chars
Preserves text width better, and conveys the essential message, namely
non-printable characters. Most people wont know what <hex code> is in
any case.
2018-06-15 08:08:21 +05:30
Kovid Goyal
5dfb8b1d80 diff kitten: Display CRLF line endings using the unicode return symbol instead of <d> as it is less intrusive.
Fixes #638
2018-06-15 07:08:57 +05:30
Kovid Goyal
82f9f002bd diff kitten: Fix default foreground/background colors not being restored when kitten quits. Fixes #637 2018-06-14 23:25:13 +05:30
Kovid Goyal
9f51506422 ... 2018-06-14 22:07:34 +05:30
Kovid Goyal
2a8295d71c diff kitten: Better error message when one of the arguments does not exist 2018-06-14 21:53:13 +05:30
Kovid Goyal
7214b66aa5 A new tui API for simple line editing
Handles basic line-editing with the extended keyboard protocol and
support for wide chars.
Currently used by the unicode input kitten.
2018-06-14 13:11:48 +05:30
Kovid Goyal
a7d9c63a4e wcwidth() for not assigned code points should be -4
That way, wcswidth() computes their width as 1, which matches how they
are rendered
2018-06-14 10:27:42 +05:30
Kovid Goyal
61dd52b50f Ignore the non-characters from the unicode standard in addition to ignoring the control characters 2018-06-14 10:20:13 +05:30
Kovid Goyal
a157f3c5e3 Fix kitty @ set-colors --all not working when more than one window present
Fixes #632
2018-06-13 14:21:55 +05:30
Kovid Goyal
fc02246373 ... 2018-06-13 07:46:11 +05:30
Kovid Goyal
adb8a4676c Fix a regression that broke the legacy increase/decrease_font_size actions 2018-06-13 07:42:12 +05:30
Kovid Goyal
f78d05a57f Clear scrollback on reset
Fixes #631
2018-06-13 07:20:19 +05:30
Kovid Goyal
301d34fecf Ignore errors in talk socket shutdown
macOS apparently shuts down the socket before shutdown can be called.
2018-06-12 21:15:43 +05:30
Kovid Goyal
0848816644 ... 2018-06-12 21:03:02 +05:30
Kovid Goyal
ba7f0132f2 Add a command line option to make --single-instance wait for the closing of the newly opened window before quitting. Fixes #630 2018-06-12 20:53:25 +05:30
Kovid Goyal
561fe81d00 Use base instead of cwd when checking for docs 2018-06-12 09:24:36 +05:30
Kovid Goyal
2db83ecf1e Run make docs for linux-package only if docs are not found 2018-06-12 09:20:18 +05:30
Kovid Goyal
1b8c6b23e1 Dont run make docs to build linux-package
The pre-built docs are now included in the official kitty source code
release. This means that building linux-package does not require
sphinx anymore.
2018-06-12 09:15:43 +05:30
Kovid Goyal
b6622b9458 Fix paths to built bundles 2018-06-12 08:53:22 +05:30
Kovid Goyal
d79e943844 Dont build docs when freezing 2018-06-12 08:41:38 +05:30
Kovid Goyal
105959d91b version 0.11.0 2018-06-12 08:36:19 +05:30
Kovid Goyal
ea414945f2 DRYer 2018-06-11 17:52:00 +05:30
Kovid Goyal
4acc692a94 Fix #627 2018-06-11 13:59:40 +05:30
Kovid Goyal
05c475e6ca Update glfw from upstream 2018-06-11 13:08:21 +05:30
Kovid Goyal
c04e1aedb2 Use basenames for default window title 2018-06-11 12:39:13 +05:30
Kovid Goyal
4711746f8f Linux: When using layouts that map the keys to non-ascii characters, map shortcuts using the ascii equivalents, from the default layout.
Fixes #606
2018-06-11 12:21:35 +05:30
Kovid Goyal
7a7262923b Fix a regression in 0.10 that caused incorrect rendering of the status bar in irssi when used inside screen.
Fixes #621. The cursor in margin check was incorrect causing upwards
movement of the cursor when below the bottom margin to be incorrect.
2018-06-11 10:13:21 +05:30
Kovid Goyal
cde60b19e8 Automatically build docs when running linux-pacakge
Fixes #622
2018-06-11 09:57:56 +05:30
Kovid Goyal
59430c1c14 Add missing screen_delete_lines to client 2018-06-11 09:12:30 +05:30
Kovid Goyal
e55cae6192 Fix kitty @ set-colors not working with the window border colors
Fixes #623
2018-06-11 09:03:24 +05:30
Kovid Goyal
ed71e73ca5 Merge branch 'master' of https://github.com/dcolascione/kitty 2018-06-11 07:18:14 +05:30
Kovid Goyal
9ffd0111cc Merge branch 'autodpi-x11' of https://github.com/dcolascione/kitty 2018-06-11 07:16:36 +05:30
Kovid Goyal
93b1919a38 Update glfw from upstream, to allow kitty to react to Xft.dpi changes 2018-06-11 07:12:06 +05:30
Daniel Colascione
535606fadb A DPI change is signaled by either a viewport _or_ a scaling factor change
Before this change, kitty considered only changes in viewport size to
signal DPI, and on non-macOS platforms, it's the scaling factor, not
the viewport size, that changes when DPI changes.
2018-06-10 18:31:44 -07:00
Kovid Goyal
7cf7d94bba Add a make docs Makefile target
Fixes #622
2018-06-11 06:31:23 +05:30
Daniel Colascione
136a718453 Avoid spurious selection
If we've previously reset the selection to EMPTY_SELECTION (say,
because we were marked dirty), the selection ends up being 0/0, so if
we click some random place, we suddenly get a non-empty selection
region and highlight random stuff even though all the user did was
left-click once somewhere.
2018-06-10 12:01:18 -07:00
Kovid Goyal
5f6c4c0f6c ... 2018-06-10 06:36:23 +05:30
Kovid Goyal
ddfe7797e5 Fix #620 2018-06-09 23:21:00 +05:30
Kovid Goyal
fc74d22724 Nicer error message when specifying a non-existent kitten 2018-06-09 13:49:33 +05:30
Kovid Goyal
ca41e642fb Nicer error message when no kitten name is specified 2018-06-09 13:47:49 +05:30
Kovid Goyal
83849ab07a Document the clipboard kitten 2018-06-09 13:32:32 +05:30
Kovid Goyal
02ac2336bd DRYer 2018-06-09 12:47:52 +05:30
Kovid Goyal
c094ebce07 Set VMIN to 1 as without it poll() returns immediately even when no data is available to read on macOS 2018-06-09 11:17:12 +05:30
Kovid Goyal
65ed9c759c ... 2018-06-09 11:10:58 +05:30
Kovid Goyal
83fe2438f9 Forgot to port the exception handler loop 2018-06-09 11:08:25 +05:30
Kovid Goyal
caa20aa081 Use the new tty code in tui as well 2018-06-09 09:06:48 +05:30
Kovid Goyal
fd598df1c6 Add a one-time support option 2018-06-08 22:17:07 +05:30
Kovid Goyal
0cc008e737 Use TTYIO for the icat kitten as well 2018-06-08 20:21:05 +05:30
Kovid Goyal
5dbaf9aab0 Clean up usage of TTYIO for remote cmds and the shell 2018-06-08 20:17:27 +05:30
Kovid Goyal
71c1432892 Update glfw from upstream 2018-06-08 18:02:44 +05:30
Kovid Goyal
59ab22c0c8 Merge branch 'master' of https://github.com/dcolascione/kitty 2018-06-08 17:53:08 +05:30
Daniel Colascione
668f6fa257 More robustly strip bracketed paste termination sequence
The previous code performed only one replacement on the bracketed
paste content. This procedure didn't stop someone embedding the
terminating sequence anyway. POC:

1) $ x=$'\033[201~'; printf '%s%s%s\necho hello world\n' "${x:0:1}" "$x" "${x:1}" | xclip
2) paste into kitty
3) see the shell execute a command!

This patch closes this hole.
2018-06-08 04:53:02 -07:00
Kovid Goyal
5701ec4082 Remove duplicate import 2018-06-08 15:42:42 +05:30
Kovid Goyal
04afe969a0 Fix building on macOS with termios.h 2018-06-08 15:41:40 +05:30
Kovid Goyal
19721014d0 Remote control: Fix kitty @ sometimes failing to read the response from kitty.
Fixes #614
2018-06-08 15:35:01 +05:30
Kovid Goyal
44b84ba295 Update glfw from upstream 2018-06-08 14:27:30 +05:30
Kovid Goyal
f80f61f84f ... 2018-06-08 12:54:38 +05:30
Kovid Goyal
6247270011 Build the docs on Travis 2018-06-08 12:03:51 +05:30
Kovid Goyal
68394cebe3 Linux: Include a man page and the HTML docs when building the linux-package 2018-06-08 11:06:41 +05:30
Kovid Goyal
a386277df9 Release a source tarball that contains pre-built docs 2018-06-08 10:36:33 +05:30
Kovid Goyal
f88b98ccd9 Generate a simple man page with the output of kitty --help 2018-06-08 10:13:15 +05:30
Kovid Goyal
eecf80469e Update glfw from upstream
Adds glfwpostemptyevent not working fix to wayland backend as well
2018-06-08 09:14:14 +05:30
Kovid Goyal
66dd83027d Use pipe2() on non-Apple platforms 2018-06-08 08:15:12 +05:30
Kovid Goyal
a1355484a6 Use the process name as the default window title rather than "kitty"
Fixes #610
2018-06-07 23:23:10 +05:30
Kovid Goyal
ca17e9b02c ... 2018-06-07 22:23:15 +05:30
Kovid Goyal
dd085a9297 Update glfw from upstream
Fixes https://github.com/glfw/glfw/issues/1281
2018-06-07 21:46:01 +05:30
Kovid Goyal
6c72c93b02 Fix inactive_text_alpha also applying to the tab bar
Fixes #612
2018-06-07 20:19:29 +05:30
Kovid Goyal
223fe85449 Mention the kitty website in kitty --help 2018-06-07 18:22:39 +05:30
Kovid Goyal
365c992f80 Fix only one the two fds created by self_pipe having CLOEXEC and NONBLOCK set 2018-06-07 15:48:50 +05:30
Kovid Goyal
1dd489cf0c Add --chop-long-lines to the less invocation as it ensures line numbers are correct even if the scrollback window is narrower 2018-06-07 13:29:31 +05:30
Kovid Goyal
ed0239a556 When viewing the scrollback in less and kitty is currently scrolled, position the scrollback in less to match kitty's scroll position.
Fixes #148
2018-06-07 13:25:37 +05:30
Kovid Goyal
dbb94fc1ff Fix wrap markers not being added when getting the history buf as text 2018-06-07 13:12:15 +05:30
Kovid Goyal
b9dd434fab Add an option to control quit on last window behavior 2018-06-07 08:03:47 +05:30
Kovid Goyal
23d089b4be macOS: Add macOS standard shortcuts
For copy, paste and new OS window (⌘+C, ⌘+V, ⌘+N)
Only defined on macOS, not Linux, thanks to the new config
infrastructure.
2018-06-07 07:54:21 +05:30
Kovid Goyal
faa5443d43 macOS: When clicking dock icon with no windows, create a new window 2018-06-07 07:19:39 +05:30
Kovid Goyal
1afa91bbb4 macOS: Keep kitty running when the last window is closed
This is how applications are supposed to behave on macOS. Fixes #543
2018-06-07 06:19:36 +05:30
Kovid Goyal
908823166e NULL check 2018-06-07 05:52:55 +05:30
Kovid Goyal
6845dd115e Add a config option to hide window decorations under X11/Wayland
Fixes #607
2018-06-07 05:48:51 +05:30
Kovid Goyal
ad44e1a515 macOS: Fix crash when triggering some global menu actions with the mouse 2018-06-06 22:53:07 +05:30
Kovid Goyal
2e8d19601b macOS: Fix the new OS window keyboard shortcut not working if no kitty window currently has focus.
Fixes #524
2018-06-06 22:18:33 +05:30
Kovid Goyal
f259c23695 Use the binary install instructions on the front-page
Testing has revealed no problems with the binary installer, so
make it live. Fixes #595
2018-06-06 17:19:05 +05:30
Kovid Goyal
f75957d8c0 ... 2018-06-06 13:31:29 +05:30
Kovid Goyal
53ae5a4f8d A new tab bar style 2018-06-06 13:27:15 +05:30
Kovid Goyal
c76a8242e2 Make the icat kitten work even if stdout/stdin are redirected 2018-06-06 12:04:10 +05:30
Kovid Goyal
8a1a45b067 Allow the diff kitten to work even if stdout/stdin are redirected
Now directly opens /dev/tty
2018-06-06 12:01:47 +05:30
Kovid Goyal
8f433f5ff6 ... 2018-06-06 11:27:03 +05:30
Kovid Goyal
97c317b735 Make screen_size handling in tui cleaner
Now screen size is fetched only for output_fd rather than sys.stdout
2018-06-06 11:20:55 +05:30
Kovid Goyal
c8e9199b03 Refactor tab drawing code 2018-06-06 09:17:31 +05:30
Kovid Goyal
351e1a5a15 Tabulate consecutive maps 2018-06-05 14:06:57 +05:30
Kovid Goyal
7b1449ba46 Include sample conf files 2018-06-05 13:57:51 +05:30
Kovid Goyal
3983953e72 Indented blocks should also be doc-commented 2018-06-05 13:54:39 +05:30
Kovid Goyal
84ad0604f4 Wrap generated conf file 2018-06-05 13:22:09 +05:30
Kovid Goyal
c509c5eb0d Add custom syntax highlighting for conf and session files 2018-06-05 12:37:04 +05:30
Kovid Goyal
d39cf5d702 Get rid of the default diff.conf 2018-06-05 10:44:18 +05:30
Kovid Goyal
2b0d8bfe46 Get rid of the default kitty.conf 2018-06-05 09:17:31 +05:30
Kovid Goyal
61fc96b187 ... 2018-06-05 08:48:37 +05:30
Kovid Goyal
070e95b5e1 Make sphinx warnings fatal 2018-06-05 08:47:34 +05:30
Kovid Goyal
727028280a ... 2018-06-05 08:46:40 +05:30
Kovid Goyal
ea4a7136f0 Get rid of the |kitty.conf| substitution 2018-06-05 08:44:51 +05:30
Kovid Goyal
065a2b37c7 Get rid of the |sc_| substitution rules 2018-06-05 08:13:28 +05:30
Kovid Goyal
b5e452942e Port changes from master 2018-06-05 07:56:41 +05:30
Kovid Goyal
4fa8cb562a Finish adding shortcuts to docs 2018-06-05 07:52:31 +05:30
Kovid Goyal
429efd70ff More conf docs 2018-06-05 07:52:31 +05:30
Kovid Goyal
4c636fdeca More conf docs 2018-06-05 07:52:31 +05:30
Kovid Goyal
1168687923 More conf docs 2018-06-05 07:52:30 +05:30
Kovid Goyal
db32079bd8 More conf docs 2018-06-05 07:52:30 +05:30
Kovid Goyal
e7246fd024 Implement shortcuts in the RST docs 2018-06-05 07:52:30 +05:30
Kovid Goyal
1c93e2a94e Show full tracebacks on sphinx failure 2018-06-05 07:52:30 +05:30
Kovid Goyal
e8bd6f4493 Initial stab at adding shortcuts to the docs 2018-06-05 07:52:30 +05:30
Kovid Goyal
cd36e3b314 oops 2018-06-05 07:52:30 +05:30
Kovid Goyal
ad29d7f639 ... 2018-06-05 07:52:30 +05:30
Kovid Goyal
b98fca972f More conf docs
Add a 🔗 role that works nicely in both contexts
2018-06-05 07:52:29 +05:30
Kovid Goyal
c63c3d2844 More conf docs 2018-06-05 07:52:29 +05:30
Kovid Goyal
6c1434ab8a More conf docs
Also dont parse option values unnecessarily
2018-06-05 07:52:29 +05:30
Kovid Goyal
b6d33e2019 More conf docs 2018-06-05 07:52:29 +05:30
Kovid Goyal
97bd8b127f More conf docs 2018-06-05 07:52:29 +05:30
Kovid Goyal
2be45eeca3 More conf docs 2018-06-05 07:52:29 +05:30
Kovid Goyal
485fe0a7fb Allow linking to latter options in a option group 2018-06-05 07:52:29 +05:30
Kovid Goyal
0070e0a58a More conf docs 2018-06-05 07:52:29 +05:30
Kovid Goyal
f7fe59dfbe Allow multiple conf files to be processed for cross-references 2018-06-05 07:52:28 +05:30
Kovid Goyal
fd731feacc Warn about missing opt references 2018-06-05 07:52:28 +05:30
Kovid Goyal
61194ef1c8 Use a custom sphinx role for the conf file 2018-06-05 07:52:28 +05:30
Kovid Goyal
dadbaf9ab1 More conf docs 2018-06-05 07:52:28 +05:30
Kovid Goyal
ffdb91ffd5 More work on the conf docs 2018-06-05 07:52:28 +05:30
Kovid Goyal
2ae3157d49 Simplify by using the option definition as a title 2018-06-05 07:52:28 +05:30
Kovid Goyal
1c2c98c1a4 More conf docs 2018-06-05 07:52:28 +05:30
Kovid Goyal
8d446d15e8 Add vim header to generated conf file 2018-06-05 07:52:28 +05:30
Kovid Goyal
faddcd34d2 More conf docs 2018-06-05 07:52:27 +05:30
Kovid Goyal
8c7e618324 Implement referring to config options 2018-06-05 07:52:27 +05:30
Kovid Goyal
cdb1fc8602 More work on conf docs 2018-06-05 07:52:27 +05:30
Kovid Goyal
9dc9fb2012 Start work on documenting the conf file 2018-06-05 07:52:27 +05:30
Kovid Goyal
3ce9e1932c Merge branch 'master' of https://github.com/ricci/kitty 2018-06-05 07:41:57 +05:30
Robert Ricci
da6ad5df45 Switch to generic hash instead of sha1 specifically
Suggested by @maximbaz
2018-06-04 15:43:03 -06:00
Robert Ricci
a9f394ce18 Fix stray text 2018-06-04 14:28:35 -06:00
Robert Ricci
093921cb9a Add support for sha1 hashes to the hint kitten
This is specifically intended for git commit hashes, so it allows for
hashes as short as 7 characters (git's default length for abbreviated
hashes) up to the full 40 characters
2018-06-04 14:25:38 -06:00
Kovid Goyal
c9818e7bb1 ... 2018-06-04 15:14:16 +05:30
Kovid Goyal
c8bf7763b6 Merge branch 'foldmethod' of https://github.com/gibfahn/kitty 2018-06-03 23:01:06 +05:30
Gibson Fahnestock
00446fa2c4 Set foldmethod in kitty.conf
As this is copied to other people's computers, it's useful to have this
set so folding always works nicely.
2018-06-03 13:36:37 +01:00
Kovid Goyal
7cf6b8b594 Move the config utils into its own package 2018-06-03 11:54:26 +05:30
Kovid Goyal
088766da72 Build the logo on Travis before running osx-bundle 2018-06-03 09:58:58 +05:30
Kovid Goyal
1c1b04b1d9 Build osx-bundle not linux-package on Travis on macOS 2018-06-03 09:41:02 +05:30
Kovid Goyal
29734563b4 Fix typo in threading.h introduced by FreeBSD commits 2018-06-03 09:29:33 +05:30
Kovid Goyal
b6e330e3e7 Remove trailing whitespace from more files 2018-06-03 09:24:15 +05:30
Kovid Goyal
138b4cf5da Fix build breakage caused by FreeBSD support
Also move the changes to the glfw files to glfw.py
2018-06-03 09:17:18 +05:30
Kovid Goyal
92e86a3b28 Merge branch 'master' of https://github.com/ricci/kitty into t 2018-06-03 08:58:37 +05:30
Robert Ricci
dc48e6c432 Clean up nested ifdefs 2018-06-02 12:48:38 -06:00
Robert Ricci
80da4d1344 Handle pthread_setname_np on FreeBSD
For some reason, the function has a slightly different name and
signature
2018-06-02 12:05:49 -06:00
Robert Ricci
e7ad2637d8 Don't 'hardcode' linux joystick for x11 module
The correct joystick module appears to be inserted in the list in
glfw.py . This fixes the build on FreeBSD 11
2018-06-02 11:46:10 -06:00
Robert Ricci
8e84b21452 Re-order includes for "_POSIX_C_SOURCE"
One FreeBSD, the build was getting errors about _POSIX_C_SOURCE
being redefined. The fix for this is to make sure that Python.h
gets included before any system libraries.
2018-06-02 11:26:02 -06:00
Kovid Goyal
5674dc5bfc ... 2018-06-02 20:24:37 +05:30
Kovid Goyal
1b9c5522fb Move tab bar into its own module 2018-06-02 20:22:27 +05:30
Kovid Goyal
71bdf73884 ... 2018-06-02 15:13:15 +05:30
Kovid Goyal
8d04216c2b Update CHANGELOG 2018-06-02 15:04:45 +05:30
Kovid Goyal
620782aa80 Document the icat kitten 2018-06-02 14:45:46 +05:30
Kovid Goyal
bd9a4e9cd8 Dont hardcode the ssh options 2018-06-02 14:30:05 +05:30
Kovid Goyal
af6ff15ee2 Wayland: Fix wheel/touchpad scrolling in opposite direction
Workaround for glfw bug. Fixes #594
2018-06-02 13:25:33 +05:30
Kovid Goyal
bb8e0e7789 Add CLI docs for the diff kitten 2018-06-02 13:09:18 +05:30
Kovid Goyal
4448d2dc7f ... 2018-06-02 13:06:49 +05:30
Kovid Goyal
7236e0ff21 Add CLI docs for the hints kitten 2018-06-02 13:04:25 +05:30
Kovid Goyal
9a615efa83 Document CLI of panel kitten 2018-06-02 12:24:21 +05:30
Kovid Goyal
f857b1afd3 Add a --debug-keyboard command line option
It's a little more discoverable than the env var.
2018-06-02 12:08:13 +05:30
Kovid Goyal
5cd9af6c37 ... 2018-06-02 11:29:07 +05:30
Kovid Goyal
ec0edd6f45 A new kitten to draw dock panels on X11 desktops showing the output from running arbitrary terminal programs 2018-06-02 11:27:01 +05:30
Kovid Goyal
0f82f90968 ... 2018-06-02 09:29:53 +05:30
Kovid Goyal
06237b85f5 Only add to server_args unconditionally if two non-option args have been seen 2018-06-01 22:55:13 +05:30
Kovid Goyal
d319180e88 ... 2018-06-01 22:48:19 +05:30
Kovid Goyal
d8f5dc958d ssh kitten: Handle combined single character options 2018-06-01 22:47:07 +05:30
Kovid Goyal
5b31b237e3 ... 2018-06-01 21:44:28 +05:30
Kovid Goyal
e211589f9f Binary install instructions 2018-06-01 21:42:49 +05:30
Kovid Goyal
cd80a85d2d ssh kitten: Dont transfer terminfo if any of the -Nnf arguments are passed to ssh 2018-06-01 20:33:57 +05:30
Kovid Goyal
b752d1ca48 Publish installer.sh 2018-06-01 20:04:15 +05:30
Kovid Goyal
091e74d618 Run the linux bundle on Travis 2018-06-01 19:22:08 +05:30
Kovid Goyal
35fb8ea1bf Linux installer 2018-06-01 14:56:05 +05:30
Kovid Goyal
b6c8b4f124 Add an INSTALL file that points to the building from source instructions 2018-06-01 14:49:26 +05:30
Kovid Goyal
9ed703384f Add some command line options to the installer 2018-06-01 11:18:29 +05:30
Kovid Goyal
e259fc5250 macOS installer implemented 2018-06-01 11:04:16 +05:30
Kovid Goyal
8b08182555 Start work on an installer 2018-06-01 10:31:52 +05:30
Kovid Goyal
4e6d929e20 Update glfw from upstream 2018-06-01 09:15:35 +05:30
Kovid Goyal
0d7feb0404 ssh kitten: Allow specifying args to pass to ssh
Uses the same syntax as the ssh command. Note that currently specifying
-N -f or similar will not work (it does not make sense anyway as you
only use this kitten if you plan on an interactive ssh session).
Fixes #591
2018-06-01 07:42:25 +05:30
Kovid Goyal
1dc9adebb4 Add an option to @set-window-title to make the change overridable 2018-06-01 06:49:47 +05:30
Kovid Goyal
d89861f601 Code to support the linux bundle 2018-05-31 23:02:38 +05:30
Kovid Goyal
0354e5b669 Add a PNG load test that does not depend on PIL 2018-05-31 20:15:23 +05:30
Kovid Goyal
98ec3fe18f Documentation work 2018-05-31 16:15:43 +05:30
Kovid Goyal
e12f5209fb Dont install libunistring on travis
No longer needed
2018-05-31 13:43:36 +05:30
Kovid Goyal
77d149518c Documentation work 2018-05-31 08:40:32 +05:30
Kovid Goyal
087f13f293 Documentation work 2018-05-31 08:30:01 +05:30
Kovid Goyal
8d8224812d Use modern GA loading code 2018-05-30 20:28:48 +05:30
Kovid Goyal
489ed6a915 Improve presentation of screenshots 2018-05-30 20:04:49 +05:30
Kovid Goyal
98bcbf8150 Partition dependency list 2018-05-30 19:45:14 +05:30
Kovid Goyal
1fe81872c6 Add the build badge to the build from source page 2018-05-30 19:43:02 +05:30
Kovid Goyal
fb0db2b7df Document kitty @ 2018-05-30 19:35:54 +05:30
Kovid Goyal
28803d9a5a Use rst roles to format cli help text 2018-05-30 19:07:12 +05:30
Kovid Goyal
fba37e73e7 ... 2018-05-30 17:52:55 +05:30
Kovid Goyal
f8fa76d77e Use program directives to namespace options 2018-05-30 15:32:50 +05:30
Kovid Goyal
a3e4c74f5d Add command line API reference 2018-05-30 15:14:07 +05:30
Kovid Goyal
0667027506 ... 2018-05-30 13:45:27 +05:30
Kovid Goyal
0c5c6fcbbe Make the sidebar a little nicer 2018-05-30 13:43:48 +05:30
Kovid Goyal
bf8dfa5ee3 Add a link to the kittens on the front page 2018-05-30 13:11:26 +05:30
Kovid Goyal
f6d0cadcc5 Redirect old asciidoc pages to new website 2018-05-30 12:52:59 +05:30
Kovid Goyal
611d95f0f7 Add the CHANGELOG to the docs 2018-05-30 12:46:00 +05:30
Kovid Goyal
bbc1430420 Add a support page
Also fix a few links flagged by make linkcheck
2018-05-30 12:08:20 +05:30
Kovid Goyal
61f7ce4dee Dont use emoji for search as it does not render well in Chrome 2018-05-30 10:42:25 +05:30
Kovid Goyal
e4163fca6a Add website publish code 2018-05-30 10:41:37 +05:30
Kovid Goyal
21d7cc1a60 Configure the alabaster theme a bit
And remove duplicated FAQ from README
2018-05-30 09:44:53 +05:30
Kovid Goyal
f82b7841c6 Start work on a kitty website
Port the current asciidoc documentation to sphinx
2018-05-29 21:03:50 +05:30
Kovid Goyal
edda0d91bb ... 2018-05-29 08:22:14 +05:30
Kovid Goyal
c70200ada0 ... 2018-05-29 08:20:11 +05:30
Kovid Goyal
adccc8fea4 When closing a tab switch to the last active tab instead of the rightmost tab. Fixes #585 2018-05-29 08:06:33 +05:30
Kovid Goyal
b886b4c6b2 Ensure font data is free'd before other finalizers
Fixes #583
2018-05-29 07:18:07 +05:30
Kovid Goyal
9eb113802d Add a setting to control the margins to the left and right of the tab bar
Fixes #584
2018-05-28 21:59:45 +05:30
Kovid Goyal
dab57f3819 When receiving contiguous non-empty OSC 52 clipboard writes, combine them.
Max combined text is set at 1MB. Allows sending texts larger than the
escape code size limit.
2018-05-28 21:50:42 +05:30
Kovid Goyal
88d0499818 If non-base64 encoded data sent via OSC 52 clear the clipboard as suggested by xterm docs 2018-05-28 21:26:59 +05:30
Kovid Goyal
51706ed195 Fix inverted permission check for write-clipboard 2018-05-28 21:18:39 +05:30
Kovid Goyal
7332561d8e Add in the redundant ctrl+<234578`> key combinations in normal and application mode. They all conflict with other keys but by implementing them the same as other terminals, I can at least avoid having to explain how they Ctrl+number is broken in traditional terminals over and over again. 2018-05-28 20:37:48 +05:30
Kovid Goyal
daafea7f7e Don't add a newline at the end of the size output 2018-05-28 14:19:12 +05:30
Kovid Goyal
5f7bc8bff2 Opening for reading is sufficient 2018-05-28 14:11:10 +05:30
Kovid Goyal
8809b8db56 Make --print-screen-size work when stdout is redirected 2018-05-28 14:09:52 +05:30
Kovid Goyal
686a5f7baf Update some missing shortcut definitions 2018-05-28 14:03:28 +05:30
Kovid Goyal
020b6b582d ... 2018-05-28 14:00:04 +05:30
Kovid Goyal
64ed8daf59 Add clipboard shortcuts to the readme 2018-05-28 13:58:57 +05:30
Kovid Goyal
dd20789249 Add support for the CSI t escape code to query window and cell sizes 2018-05-28 13:47:42 +05:30
Kovid Goyal
6570565e2d Rename --print-screen-size to --print-window-size
We are reporting the size (inner area) of the terminal window, not the
full screen.
2018-05-28 13:14:42 +05:30
Kovid Goyal
345b3437f4 Add a config option to set the EDITOR kitty uses
Useful on systems that make it hard to set system-wide environment
variables. See #580
2018-05-28 12:24:09 +05:30
Kovid Goyal
20611c7c15 Add a convenience option to kitty icat to print the screen size 2018-05-28 10:11:19 +05:30
Kovid Goyal
8dea5b3e3e Reduce data sent to GPU per draw by 30%
Split up the Cell structure into a CPUCell and a GPUCell. Only the
GPUCell part needs to be sent to the GPU. Should make kitty use even
less system resources and make a performance difference on systems where
the GPU bandwidth is constrained.

Also allows adding more CPU only data in the future without affecting
GPU bandwidth. For example, hyperlinks or more combining characters.
2018-05-27 21:25:09 +05:30
Kovid Goyal
c8756612f7 ... 2018-05-27 12:37:50 +05:30
Kovid Goyal
9fe995c7d8 Workaround for GLFW not working with the wlroots wayland compositor 2018-05-27 12:37:50 +05:30
Kovid Goyal
422bcfcb54 ... 2018-05-27 12:37:50 +05:30
Kovid Goyal
3b04776956 Show the window much sooner after creating it
Do not make any OpenGL calls between creation and showing of the window.
Hopefully, that will make the fussy wlroots happy.
2018-05-27 12:37:50 +05:30
Kovid Goyal
1ba76508de More detailed error message when cursor creation fails 2018-05-27 12:37:49 +05:30
Kovid Goyal
4e09a518d5 Don't initialize OpenGL for the temp window as it is not needed
Also dont fail to start if cursor creation fails, since cursor shapes
are not critical.
2018-05-27 12:37:49 +05:30
Kovid Goyal
e056460f0b Split up creation of font group and sending of sprites to GPU 2018-05-27 12:37:49 +05:30
Kovid Goyal
da166aef99 API to control smallest allowed resize from python 2018-05-27 12:37:49 +05:30
Kovid Goyal
a42bc3bf8c oops 2018-05-27 12:37:49 +05:30
Kovid Goyal
997c244664 Allow specifying initial window size in number of cells 2018-05-27 12:37:49 +05:30
Kovid Goyal
fe8cf46d56 Handle window DPI changes 2018-05-27 12:37:49 +05:30
Kovid Goyal
df9eab279a Implement changing the font size for individual top level (OS) windows 2018-05-27 12:37:49 +05:30
Kovid Goyal
b14c9614e0 DRYer 2018-05-27 12:37:05 +05:30
Kovid Goyal
ed44515318 All tests pass 2018-05-27 12:37:05 +05:30
Kovid Goyal
523aadaa3b Refactor font group handling
Allow kitty to manage multiple groups of fonts with different cell
sizes. Will eventually allow kitty to have different font sizes/dpi per
OSWindow
2018-05-27 12:37:05 +05:30
Kovid Goyal
4f18342ea6 Linux: Fix fonts not being correctly read from TrueType Collection (.ttc) files.
kitty was always selecting the first font rather than the
actual matched font. Fixes #577
2018-05-27 12:30:28 +05:30
Kovid Goyal
f36e6f0461 Nicer handling of empty lines 2018-05-26 13:40:43 +05:30
Kovid Goyal
978dc008e2 hints kitten: Fix a regression that caused some blank lines to be not displayed 2018-05-26 13:35:44 +05:30
Kovid Goyal
94f1759b14 Update README 2018-05-24 19:31:08 +05:30
154 changed files with 39140 additions and 32820 deletions

2
.gitignore vendored
View File

@@ -13,3 +13,5 @@ kitty-profile
dev
__pycache__
glfw/wayland-*-client-protocol.[ch]
docs/_build
docs/generated

View File

@@ -1,6 +1,23 @@
git:
depth: false
matrix:
fast_finish: true
include:
- os: linux
dist: trusty
group: beta
language: python
sudo: false
env: USE_BUNDLE=1 SW=$HOME/sw PKG_CONFIG_PATH=$SW/lib/pkgconfig LD_LIBRARY_PATH=$SW/lib PYTHON=$SW/bin/python3
addons:
apt:
packages:
- libxi-dev
- libxrandr-dev
- libxinerama-dev
- libxcursor-dev
- libxcb-xkb-dev
- os: linux
dist: trusty
group: beta
@@ -18,7 +35,6 @@ matrix:
- libxrandr-dev
- libxinerama-dev
- libxcursor-dev
- libunistring-dev
- libxcb-xkb-dev
- libpng-dev
- python3-pil
@@ -40,7 +56,6 @@ matrix:
- libxrandr-dev
- libxinerama-dev
- libxcursor-dev
- libunistring-dev
- libxcb-xkb-dev
- libpng-dev
- python3-pil
@@ -50,7 +65,7 @@ matrix:
group: beta
sudo: false
env:
- RUN_FLAKE=1 BUILD_PKG=1
- RUN_FLAKE=1 BUILD_PKG=linux-package
language: python
python: "3.6"
addons:
@@ -62,7 +77,6 @@ matrix:
- libxrandr-dev
- libxinerama-dev
- libxcursor-dev
- libunistring-dev
- libxcb-xkb-dev
- libpng-dev
- python3-pil
@@ -73,7 +87,7 @@ matrix:
- os: osx
language: generic
env: USE_BREW=1 BUILD_PKG=1
env: USE_BREW=1 BUILD_PKG=osx-bundle
env:
global:
@@ -90,26 +104,40 @@ install: |
brew update;
brew upgrade python;
brew install harfbuzz --without-glib --without-gobject-introspection --without-graphite2 --without-icu4c --without-freetype;
brew install imagemagick
brew install optipng
brew install librsvg
logo/make.py
else
mkdir -p $SW;
curl https://download.calibre-ebook.com/travis/kitty/osx.tar.xz | tar xJ -C $SW;
fi
else
pushd /tmp
wget --no-check-certificate http://xkbcommon.org/download/libxkbcommon-0.6.1.tar.xz
tar xf libxkbcommon-0.6.1.tar.xz
cd libxkbcommon-0.6.1
./configure --prefix=$HOME/harfbuzz --disable-dependency-tracking --disable-static
make -j2
make install
wget https://github.com/behdad/harfbuzz/releases/download/1.6.3/harfbuzz-1.6.3.tar.bz2
tar xf harfbuzz-1.6.3.tar.bz2
cd harfbuzz-1.6.3
./configure --prefix=$HOME/harfbuzz --disable-dependency-tracking --disable-static --with-glib=no --with-gobject=no --with-cairo=no --with-fontconfig=no --with-icu=no --with-directwrite=no --with-freetype=yes
make -j2
make install
cd ..
popd
if [[ "$USE_BUNDLE" == "1" ]]; then
mkdir -p $SW;
curl https://download.calibre-ebook.com/travis/kitty/linux-64.tar.xz | tar xJ -C $SW;
find $SW -type f -name '*.pc' -exec sed -i "s,/sw/sw,$SW,g" {} \;;
find $SW -type f -name '_sysconfig*.py' -exec sed -i "s,/sw/sw,$SW,g" {} \;;
else
pushd /tmp
wget --no-check-certificate http://xkbcommon.org/download/libxkbcommon-0.6.1.tar.xz
tar xf libxkbcommon-0.6.1.tar.xz
cd libxkbcommon-0.6.1
./configure --prefix=$HOME/harfbuzz --disable-dependency-tracking --disable-static
make -j2
make install
wget https://github.com/behdad/harfbuzz/releases/download/1.6.3/harfbuzz-1.6.3.tar.bz2
tar xf harfbuzz-1.6.3.tar.bz2
cd harfbuzz-1.6.3
./configure --prefix=$HOME/harfbuzz --disable-dependency-tracking --disable-static --with-glib=no --with-gobject=no --with-cairo=no --with-fontconfig=no --with-icu=no --with-directwrite=no --with-freetype=yes
make -j2
make install
cd ..
popd
fi
if [[ "$BUILD_PKG" == "linux-package" ]]; then
pip install sphinx
fi
fi
pkg-config --cflags harfbuzz
if [[ "$TRAVIS_OS_NAME" != 'osx' ]]; then
@@ -119,11 +147,20 @@ install: |
set +e
before_script:
- echo $LD_LIBRARY_PATH
- echo LD_LIBRARY_PATH=$LD_LIBRARY_PATH PKG_CONFIG_PATH=$PKG_CONFIG_PATH
- $PYTHON setup.py build --debug --verbose $SANITIZE_ARG;
script:
- if grep -Inr '\s$' kitty kitty_tests *.py *.asciidoc .gitattributes .gitignore; then echo Trailing whitespace found, aborting.; exit 1; fi
- if [[ -z $SANITIZE_ARG ]]; then $PYTHON test.py; else ./asan-launcher test.py; fi
- if [[ "$RUN_FLAKE" == "1" ]]; then flake8 --count .; fi
- if [[ "$BUILD_PKG" == "1" ]]; then $PYTHON setup.py linux-package; fi
script: |
set -e
if grep -Inr '\s$' kitty kitty_tests kittens docs *.py *.asciidoc *.rst .gitattributes .gitignore; then echo Trailing whitespace found, aborting.; exit 1; fi
if [[ -z "$SANITIZE_ARG" ]]; then $PYTHON test.py; else ./asan-launcher test.py; fi
if [[ "$RUN_FLAKE" == "1" ]]; then flake8 --count .; fi
if [[ "$BUILD_PKG" == "linux-package" ]]; then make FAIL_WARN=-W man && make FAIL_WARN=-W html; fi
if [[ ! -z "$BUILD_PKG" ]]; then $PYTHON setup.py $BUILD_PKG; fi
set +e

View File

@@ -1,567 +1 @@
Changelog
==============
kitty is a feature full, cross-platform, *fast*, GPU based terminal emulator.
version 0.10.1 [2018-05-24]
------------------------------
- Add a kitten to easily ssh into servers that automatically copies the
terminfo files over. ``kitty +kitten ssh myserver``.
- diff kitten: Make the keyboard shortcuts configurable
(`#563 <https://github.com/kovidgoyal/kitty/issues/563>`_)
- Allow controlling *background_opacity* via either keyboard shortcuts or
remote control. Note that you must set *dynamic_background_opacity yes* in
kitty.conf first. (`#569 <https://github.com/kovidgoyal/kitty/issues/569>`_)
- diff kitten: Add keybindings to scroll by page
- diff kitten: Fix incorrect syntax highlighting for a few file formats such as
yaml
- macOS: Fix regression that caused the *macos_option_as_alt* setting to always
be disabled for all OS windows in a kitty instance after the first window
(`#571 <https://github.com/kovidgoyal/kitty/issues/571>`_)
- Fix Ctrl+Alt+Space not working in normal and application keyboard modes
(`#562 <https://github.com/kovidgoyal/kitty/issues/562>`_)
version 0.10.0 [2018-05-21]
------------------------------
- A diff kitten to show side-by-side diffs with syntax highlighting and support
for images. See `diff README
<https://github.com/kovidgoyal/kitty/blob/master/kittens/diff/README.asciidoc>`_.
- Make windows in the various kitty layouts manually resizable. See the
`Layouts section of the README
<https://github.com/kovidgoyal/kitty#layouts>`_ for details.
- Implement support for the SGR *faint* escape code to make text blend
into the background (`#446 <https://github.com/kovidgoyal/kitty/issues/446>`_).
- Make the hints kitten a little `smarter
<https://github.com/kovidgoyal/kitty/commit/ad1109b6fe0a6802ca4f77182a7a0b36086b3e9f>`_,
so that URLs that stretch over multiple lines are detected. Also improve
detection of surrounding brackets/quotes.
- Make the kitty window id available as the environment variable
`KITTY_WINDOW_ID` (`#532 <https://github.com/kovidgoyal/kitty/issues/532>`_).
- Add a "fat" layout that is similar to the "tall" layout but vertically
oriented.
- Expand environment variables in config file include directives
- Allow programs running in kitty to `read/write from the clipboard
<https://github.com/kovidgoyal/kitty/commit/889ca7791244253cb08fbc3eca8883a87fb943a7>`_.
By default only writing is allowed. This feature is supported in many
terminals, search for `OSC 52 clipboard` to find out more about using it.
- Fix moving cursor outside a defined page area incorrectly causing the cursor
to be placed inside the page area. Caused incorrect rendering in neovim, in
some situations (`#542 <https://github.com/kovidgoyal/kitty/issues/542>`_).
- Render a couple more powerline symbols directly, bypassing the font
(`#550 <https://github.com/kovidgoyal/kitty/issues/550>`_).
- Fix ctrl+alt+<special> not working in normal and application keyboard (`#548
<https://github.com/kovidgoyal/kitty/issues/548>`_).
- Partial fix for rendering Right-to-left languages like Arabic. Rendering of
Arabic is never going to be perfect, but now it is at least readable.
- Fix Ctrl+backspace acting as plain backspace in normal and application
keyboard modes (`#538 <https://github.com/kovidgoyal/kitty/issues/538>`_).
- Have the paste_from_selection action paste from the clipboard on platforms
that do not have a primary selection such as Wayland and macOS
(`#529 <https://github.com/kovidgoyal/kitty/issues/529>`_)
- Fix cursor_stop_blinking_after=0 not working (`#530 <https://github.com/kovidgoyal/kitty/issues/530>`_)
version 0.9.1 [2018-05-05]
------------------------------
- Show a bell symbol on the tab if a bell occurs in one of the windows in the tab and
the window is not the currently focused window
- Change the window border color if a bell occurs in an unfocused window. Can
be disabled by setting the bell_border_color to be the same as the
inactive_border_color.
- macOS: Add support for dead keys
- Unicode input: When searching by name search for prefix matches as well as
whole word matches
- Dynamically allocate the memory used for the scrollback history buffer.
Reduces startup memory consumption when using very large scrollback
buffer sizes.
- Add an option to not request window attention on bell.
- Remote control: Allow matching windows by number (visible position).
- macOS: Fix changing tab title and kitty shell not working
- When triple-clicking select all wrapped lines belonging to a single logical line.
- hints kitten: Detect bracketed URLs and dont include the closing bracket in the URL.
- When calling pass_selection_to_program use the current directory of the child
process as the cwd of the program.
- Add macos_hide_from_tasks option to hide kitty from the macOS task switcher
- macOS: When the macos_titlebar_color is set to background change the titlebar
colors to match the current background color of the active kitty window
- Add a setting to clear all shortcuts defined up to that point in the config
file(s)
- Add a setting (kitty_mod) to change the modifier used by all the default
kitty shortcuts, globally
- Fix Shift+function key not working
- Support the F13 to F25 function keys
- Don't fail to start if the user deletes the hintstyle key from their
fontconfig configuration.
- When rendering a private use unicode codepoint and a space as a two cell
ligature, set the foreground colors of the space cell to match the colors of
the first cell. Works around applications like powerline that use different
colors for the two cells.
- Fix passing @text to other programs such as when viewing the scrollback
buffer not working correctly if kitty is itself scrolled up.
- Fix window focus gained/lost events not being reported to child programs when
switching windows/tabs using the various keyboard shortcuts.
- Fix tab title not changing to reflect the window title when switching between different windows in a tab
- Ignore -e if it is specified on the command line. This is for compatibility
with broken software that assumes terminals should run with an -e option to
execute commands instead of just passing the commands as arguments.
version 0.9.0 [2018-04-15]
------------------------------
- A new kitty command shell to allow controlling kitty via commands. Press
`ctrl+shift+escape` to run the shell.
- The hints kitten has become much more powerful. Now in addition to URLs you
can use it to select word, paths, filenames, lines, etc. from the screen.
These can be inserted into the terminal, copied to clipboard or sent to
external programs.
- Linux: Switch to libxkbcommon for keyboard handling. It allows kitty to
support XCompose and dead keys and also react to keyboard remapping/layout
change without needing a restart.
- Add support for multiple-key-sequence shortcuts
- A new remote control command `set-colors` to change the current and/or
configured colors.
- When double-clicking to select a word, select words that continue onto the
next/prev line as well.
- Add an `include` directive for the config files to read multiple config files
- Improve mouse selection for windows with padding. Moving the mouse into the
padding area now acts as if the mouse is over the nearest cell.
- Allow setting all 256 terminal colors in the config file
- Fix using `kitty --single-instance` to open a new window in a running kitty
instance, not respecting the `--directory` flag
- URL hints: Exclude trailing punctuation from URLs
- URL hints: Launch the browser from the kitty parent process rather than the
hints kitten. Fixes launching on some systems where xdg-open doesn't like
being run from a kitten.
- Allow using rectangle select mode by pressing shift in addition to the
rectangle select modifiers even when the terminal program has grabbed the
mouse.
version 0.8.4 [2018-03-31]
-----------------------------
- Fix presence of XDG_CONFIG_DIRS and absence of XDG_CONFIG_HOME preventing
kitty from starting
- Revert change in last release to cell width calculation. Instead just clip
the right edges of characters that overflow the cell by at most two pixels
version 0.8.3 [2018-03-29]
-----------------------------
- Fix a regression that broke the visual bell and invert screen colors escape
code
- Allow double-click and triple-click + drag to extend selections word at a
time or line at a time
- Add a keyboard shortcut to set the tab title
- Fix setting window title to empty via OSC escape code not working correctly
- Linux: Fix cell width calculation incorrect for some fonts (cell widths are
now calculated by actually rendering bitmaps, which is slower but more
accurate)
- Allow specifying a system wide kitty config file, for all users
- Add a --debug-config command line flag to output data about the system and
kitty configuration.
- Wayland: Fix auto-repeat of keys not working
version 0.8.2 [2018-03-17]
-----------------------------
- Allow extending existing selections by right clicking
- Add a configurable keyboard shortcut and remote command to set the font size to a specific value
- Add an option to have kitty close the window when the main processes running in it exits, even if there are still background processes writing to that terminal
- Add configurable keyboard shortcuts to switch to a specific layout
- Add a keyboard shortcut to edit the kitty config file easily
- macOS: Fix restoring of window size not correct on Retina screens
- macOS: Add a facility to specify command line arguments when running kitty from the GUI
- Add a focus-tab remote command
- Fix screen not being refreshed immediately after moving a window.
- Fix a crash when getting the contents of the scrollback buffer as text
version 0.8.1 [2018-03-09]
-----------------------------
- Extend kitty's remote control feature to work over both UNIX and TCP sockets,
so now you can control kitty from across the internet, if you want to.
- Render private use unicode characters that are followed by a space as a two
character ligature. This fixes rendering for applications that misuse
private-use characters to display square symbols.
- Fix Unicode emoji presentation variant selector causing new a fallback font
instance to be created
- Fix a rare error that prevented the Unicode input kitten from working
sometimes
- Allow using Ctrl+Alt+letter in legacy keyboard modes by outputting them as Ctrl+letter and Alt+letter.
This matches other terminals' behavior.
- Fix cursor position off-by-one on horizontal axis when resizing the terminal
- Wayland: Fix auto-repeat of keys not working
- Wayland: Add support for window decorations provided by the Wayland shell
- macOS: Fix URL hints not working
- macOS: Fix shell not starting in login mode on some computers
- macOS: Output errors into console.app when running as a bundle
version 0.8.0 [2018-02-24]
-----------------------------
- A framework for kittens, that is, small terminal programs designed to run
inside kitty and extend its capabilities. Examples include unicode input and
selecting URLs with the keyboard.
- Input arbitrary unicode characters by pressing Ctrl+Shift+u. You can choose
characters by name, by hex code, by recently used, etc. There is even and
editable Favorites list.
- Open URLs using only the keyboard. kitty has a new "hints mode". Press
Ctrl+Shift+e and all detected URLs on the screen are highlighted with a key
to press to open them. The facility is customizable so you can change
what is detected as a URL and which program is used to open it.
- Add an option to change the titlebar color of kitty windows on macOS
- Only consider Emoji characters with default Emoji presentation to be two
cells wide. This matches the standard. Also add support for the Unicode Emoji
variation presentation selector.
- Prevent video tearing during high speed scrolling by syncing draws
to the monitor's refresh rate. There is a new configuration option to
control this ``sync_to_monitor``.
- When displaying only a single window, use the default background color of the
window (which can be changed via escape codes) as the color for the margin
and padding of the window.
- Add some non standard terminfo capabilities used by neovim and tmux.
- Fix large drop in performance when using multiple top-level windows on macOS
- Fix save/restore of window sizes not working correctly.
- Remove option to use system wcwidth(). Now always use a wcwidth() based on
the Unicode standard. Only sane way.
- Fix a regression that caused a few ligature glyphs to not render correctly in
rare circumstances.
- Browsing the scrollback buffer now happens in an overlay window instead of a
new window/tab.
version 0.7.1 [2018-01-31]
---------------------------
- Add an option to adjust the width of character cells
- Fix selecting text with the mouse in the scrollback buffer selecting text
from the line above the actually selected line
- Fix some italic fonts having the right edge of characters cut-off,
unnecessarily
version 0.7.0 [2018-01-24]
---------------------------
- Allow controlling kitty from the shell prompt/scripts. You can
open/close/rename windows and tabs and even send input to specific windows.
See the README for details.
- Add option to put tab bar at the top instead of the bottom
- Add option to override the default shell
- Add "Horizontal" and "Vertical" window layouts
- Sessions: Allow setting titles and working directories for individual windows
- Option to copy to clipboard on mouse select
- Fix incorrect reporting of mouse move events when using the SGR protocol
- Make alt+backspace delete the previous word
- Take the mouse wheel multiplier option in to account when generating fake key
scroll events
- macOS: Fix closing top-level window does not transfer focus to other
top-level windows.
- macOS: Fix alt+arrow keys not working when disabling the macos_option_as_alt
config option.
- kitty icat: Workaround for bug in ImageMagick that would cause some images
to fail to display at certain sizes.
- Fix rendering of text with ligature fonts that do not use dummy glyphs
- Fix a regression that caused copying of the selection to clipboard to only
copy the visible part of the selection
- Fix incorrect handling of some unicode combining marks that are not re-ordered
- Fix handling on non-BMP combining characters
- Drop the dependency on libunistring
version 0.6.1 [2017-12-28]
---------------------------
- Add an option to fade the text in inactive windows
- Add new actions to open windows/tabs/etc. with the working directory set to
the working directory of the current window.
- Automatically adjust cell size when DPI changes, for example when kitty is
moved from one monitor to another with a different DPI
- Ensure underlines are rendered even for fonts with very poor metrics
- Fix some emoji glyphs not colored on Linux
- Internal wcwidth() implementation is now auto-generated from the unicode
standard database
- Allow configuring the modifiers to use for rectangular selection with the
mouse.
- Fix incorrect minimum wayland version in the build script
- Fix a crash when detecting a URL that ends at the end of the line
- Fix regression that broke drawing of hollow cursor when window loses focus
version 0.6.0 [2017-12-18]
---------------------------
- Support background transparency via the background_opacity option. Provided
that your OS/window manager supports transparency, you can now have kitty
render pixels that have only the default background color as
semi-transparent.
- Support multiple top level (OS) windows. These windows all share the sprite
texture cache on the GPU, further reducing overall resource usage. Use
the shortcut `ctrl+shift+n` to open a new top-level window.
- Add support for a *daemon* mode using the `--single-instance` command line
option. With this option you can have only a single kitty instance running.
All future invocations simply open new top-level windows in the existing
instance.
- Support colored emoji
- Use CoreText instead of FreeType to render text on macOS
- Support running on the "low power" GPU on dual GPU macOS machines
- Add a new "grid" window layout
- Drop the dependency on glfw (kitty now uses a modified, bundled copy of glfw)
- Add an option to control the audio bell volume on X11 systems
- Add a command line switch to set the name part of the WM_CLASS window
property independently.
- Add a command line switch to set the window title.
- Add more options to customize the tab-bar's appearance (font styles and
separator)
- Allow drag and drop of files into kitty. On drop kitty will paste the
file path to the running program.
- Add an option to control the underline style for URL highlighting on hover
- X11: Set the WINDOWID environment variable
- Fix middle and right buttons swapped when sending mouse events to child
processes
- Allow selecting in a rectangle by holding down Ctrl+Alt while dragging with
the mouse.
version 0.5.1 [2017-12-01]
---------------------------
- Add an option to control the thickness of lines in box drawing characters
- Increase max. allowed ligature length to nine characters
- Fix text not vertically centered when adjusting line height
- Fix unicode block characters not being rendered properly
- Fix shift+up/down not generating correct escape codes
- Image display: Fix displaying images taller than two screen heights not
scrolling properly
version 0.5.0 [2017-11-19]
---------------------------
- Add support for ligature fonts such as Fira Code, Hasklig, etc. kitty now
uses harfbuzz for text shaping which allow it to support advanced OpenType
features such as contextual alternates/ligatures/combining glyphs/etc.
- Make it easy to select fonts by allowing listing of monospace fonts using:
kitty list-fonts
- Add an option to have window focus follow mouse
- Add a keyboard shortcut (ctrl+shift+f11) to toggle fullscreen mode
- macOS: Fix handling of option key. It now behaves just like the alt key on
Linux. There is an option to make it type unicode characters instead.
- Linux: Add support for startup notification on X11 desktops. kitty will
now inform the window manager when its startup is complete.
- Fix shell prompt being duplicated when window is resized
- Fix crash when displaying more than 64 images in the same session
- Add support for colons in SGR color codes. These are generated by some
applications such as neovim when they mistakenly identify kitty as a libvte
based terminal.
- Fix mouse interaction not working in apps using obsolete mouse interaction
protocols
- Linux: no longer require glew as a dependency
version 0.4.2 [2017-10-23]
---------------------------
- Fix a regression in 0.4.0 that broke custom key mappings
- Fix a regression in 0.4.0 that broke support for non-QWERTY keyboard layouts
- Avoid using threads to reap zombie child processes. Also prevent kitty from
hanging if the open program hangs when clicking on a URL.
version 0.4.0 [2017-10-22]
---------------------------
- Support for drawing arbitrary raster graphics (images) in the terminal via a
new graphics protocol. kitty can draw images with full 32-bit color using both
ssh connections and files/shared memory (when available) for better
performance. The drawing primitives support alpha blending and z-index.
Images can be drawn both above and below text. See
https://github.com/kovidgoyal/kitty/blob/master/graphics-protocol.asciidoc
for details.
- Refactor kitty's internals to make it even faster and more efficient. The CPU
usage of kitty + X server while doing intensive tasks such as scrolling a
file continuously in less has been reduced by 50%. There are now two
configuration options ``repaint_delay`` and ``input_delay`` you can use to
fine tune kitty's performance vs CPU usage profile. The CPU usage of kitty +
X when scrolling in less is now significantly better than most (all?) other
terminals. See https://github.com/kovidgoyal/kitty#performance
- Hovering over URLs with the mouse now underlines them to indicate they
can be clicked. Hold down Ctrl+Shift while clicking to open the URL.
- Selection using the mouse is now more intelligent. It does not add
blank cells (i.e. cells that have no content) after the end of text in a
line to the selection.
- The block cursor in now fully opaque but renders the character under it in
the background color, for enhanced visibility.
- Allow combining multiple independent actions into a single shortcut
- Add a new shortcut to pass the current selection to an external program
- Allow creating shortcuts to open new windows running arbitrary commands. You
can also pass the current selection to the command as an arguments and the
contents of the screen + scrollback buffer as stdin to the command.
See https://sw.kovidgoyal.net/kitty/changelog.html

7
INSTALL.md Normal file
View File

@@ -0,0 +1,7 @@
To build from source:
https://sw.kovidgoyal.net/kitty/build.html
Pre-built binaries:
https://sw.kovidgoyal.net/kitty/binary.html

View File

@@ -32,3 +32,14 @@ rendered_logo: logo/kitty.iconset/icon_256x256.png
app: rendered_logo
python3 setup.py kitty.app $(VVAL)
man:
$(MAKE) FAIL_WARN=$(FAIL_WARN) -C docs man
html:
$(MAKE) FAIL_WARN=$(FAIL_WARN) -C docs html
linkcheck:
$(MAKE) FAIL_WARN=$(FAIL_WARN) -C docs linkcheck
docs: man html

View File

@@ -1,634 +1,5 @@
= kitty - A terminal emulator
:toc:
:toc-placement!:
// START_SHORTCUT_BLOCK
:sc_close_tab: pass:quotes[`ctrl+shift+q`]
:sc_close_window: pass:quotes[`ctrl+shift+w`]
:sc_copy_to_clipboard: pass:quotes[`ctrl+shift+c`]
:sc_decrease_font_size: pass:quotes[`ctrl+shift+minus`]
:sc_edit_config_file: pass:quotes[`ctrl+shift+f2`]
:sc_eighth_window: pass:quotes[`ctrl+shift+8`]
:sc_fifth_window: pass:quotes[`ctrl+shift+5`]
:sc_first_window: pass:quotes[`ctrl+shift+1`]
:sc_fourth_window: pass:quotes[`ctrl+shift+4`]
:sc_increase_font_size: pass:quotes[`ctrl+shift+equal`]
:sc_input_unicode_character: pass:quotes[`ctrl+shift+u`]
:sc_kitten_hints: pass:quotes[`ctrl+shift+e`]
:sc_kitten_hints_type_line_program: pass:quotes[`ctrl+shift+p → l`]
:sc_kitten_hints_type_path: pass:quotes[`ctrl+shift+p → shift+f`]
:sc_kitten_hints_type_path_program: pass:quotes[`ctrl+shift+p → f`]
:sc_kitten_hints_type_word_program: pass:quotes[`ctrl+shift+p → w`]
:sc_kitty_shell_window: pass:quotes[`ctrl+shift+escape`]
:sc_move_tab_backward: pass:quotes[`ctrl+shift+,`]
:sc_move_tab_forward: pass:quotes[`ctrl+shift+.`]
:sc_move_window_backward: pass:quotes[`ctrl+shift+b`]
:sc_move_window_forward: pass:quotes[`ctrl+shift+f`]
:sc_move_window_to_top: pass:quotes[`ctrl+shift+``]
:sc_new_os_window: pass:quotes[`ctrl+shift+n`]
:sc_new_tab: pass:quotes[`ctrl+shift+t`]
:sc_new_window: pass:quotes[`ctrl+shift+enter`]
:sc_next_layout: pass:quotes[`ctrl+shift+l`]
:sc_next_tab: pass:quotes[`ctrl+shift+right`]
:sc_next_window: pass:quotes[`ctrl+shift+]`]
:sc_ninth_window: pass:quotes[`ctrl+shift+9`]
:sc_pass_selection_to_program: pass:quotes[`ctrl+shift+o`]
:sc_paste_from_clipboard: pass:quotes[`ctrl+shift+v`]
:sc_paste_from_selection: pass:quotes[`ctrl+shift+s` or `shift+insert`]
:sc_previous_tab: pass:quotes[`ctrl+shift+left`]
:sc_previous_window: pass:quotes[`ctrl+shift+[`]
:sc_restore_font_size: pass:quotes[`ctrl+shift+backspace`]
:sc_scroll_end: pass:quotes[`ctrl+shift+end`]
:sc_scroll_home: pass:quotes[`ctrl+shift+home`]
:sc_scroll_line_down: pass:quotes[`ctrl+shift+down` or `ctrl+shift+j`]
:sc_scroll_line_up: pass:quotes[`ctrl+shift+up` or `ctrl+shift+k`]
:sc_scroll_page_down: pass:quotes[`ctrl+shift+page_down`]
:sc_scroll_page_up: pass:quotes[`ctrl+shift+page_up`]
:sc_second_window: pass:quotes[`ctrl+shift+2`]
:sc_set_background_opacity_0_1: pass:quotes[`ctrl+shift+a → l`]
:sc_set_background_opacity_1: pass:quotes[`ctrl+shift+a → 1`]
:sc_set_background_opacity_default: pass:quotes[`ctrl+shift+a → d`]
:sc_set_background_opacity_plus0_1: pass:quotes[`ctrl+shift+a → m`]
:sc_set_tab_title: pass:quotes[`ctrl+shift+alt+t`]
:sc_seventh_window: pass:quotes[`ctrl+shift+7`]
:sc_show_scrollback: pass:quotes[`ctrl+shift+h`]
:sc_sixth_window: pass:quotes[`ctrl+shift+6`]
:sc_start_resizing_window: pass:quotes[`ctrl+shift+r`]
:sc_tenth_window: pass:quotes[`ctrl+shift+0`]
:sc_third_window: pass:quotes[`ctrl+shift+3`]
:sc_toggle_fullscreen: pass:quotes[`ctrl+shift+f11`]
// END_SHORTCUT_BLOCK
= kitty - the fast, featureful, GPU based, terminal emulator
See https://sw.kovidgoyal.net/kitty
++++
<img align="left" role="left" src="logo/kitty.png?raw=true"/>
++++
image::https://travis-ci.org/kovidgoyal/kitty.svg?branch=master[Build status, link=https://travis-ci.org/kovidgoyal/kitty]
== Major Features
* Uses OpenGL for rendering, offloads rendering to the GPU for link:#performance[lower system
load] and buttery smooth scrolling. Uses threaded rendering to minimize input
latency.
* Supports all modern terminal features: link:graphics-protocol.asciidoc[graphics (images)], unicode,
true-color, OpenType ligatures, mouse protocol, focus tracking, bracketed paste and so on.
* Supports tiling multiple terminal windows side by side in different
link:#layouts[layouts] without needing to use an extra program like tmux
* Can be link:remote-control.asciidoc[controlled from scripts or the shell prompt], even over SSH.
* Has a framework for _kittens_, small terminal programs that can be used to extend kitty's functionality.
For example, they are used for link:#unicode-input[Unicode input], link:#hints[Hints] and link:https://github.com/kovidgoyal/kitty/blob/master/kittens/diff/README.asciidoc[Side-by-side diff].
* Supports link:#startup-sessions[startup sessions] which allow you to specify the window/tab layout,
working directories and programs to run on startup.
* Cross-platform support: kitty currently works on Linux and macOS, but because
it uses only OpenGL for rendering, it should be trivial to port to other
platforms.
* Allows you to open link:#the-scrollback-buffer[the scrollback buffer] in a separate
window using arbitrary programs of your choice. This is useful for browsing
the history comfortably in a pager or editor.
image::screenshots/screenshot.png?raw=true[Screenshot, showing three programs in the "Tall" layout]
toc::[]
== Installation
kitty is designed to run from source, for easy hackability. Make sure
the following dependencies are installed first.
=== Dependencies
* python >= 3.5
* harfbuzz >= 1.5.0
* zlib
* libpng
* freetype (not needed on macOS)
* fontconfig (not needed on macOS)
* ImageMagick (optional, needed to use the `kitty icat` tool to display images in the terminal)
* pygments (optional, need for syntax highlighting in `kitty +kitten diff`)
* gcc or clang (required only for building)
* pkg-config (required only for building)
* For building on Linux in addition to the above dependencies you might also need to install the `-dev` packages for `xcursor`, `xrandr`, `libxi`, `xinerama`, `libgl1-mesa` and `xkbcommon-x11`, if they are not already installed by your distro.
=== Install and run from source
....
git clone https://github.com/kovidgoyal/kitty && cd kitty
....
Now build the native code parts of kitty with the following command:
....
make
....
You can run kitty, as:
....
python3 .
....
If that works, you can create a script to launch kitty:
....
#!/usr/bin/env python3
import runpy
runpy.run_path('/path/to/kitty/dir', run_name='__main__')
....
And place it in `~/bin` or `/usr/bin` so that you can run kitty using
just `kitty`.
=== Linux packages
* Arch Linux: https://www.archlinux.org/packages/community/x86_64/kitty/
* Arch Linux: AUR git package https://aur.archlinux.org/packages/kitty-git/
* NixOS / nixpkgs: https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/misc/kitty/default.nix
* Gentoo: https://packages.gentoo.org/packages/x11-terms/kitty
* openSUSE: https://build.opensuse.org/package/show/X11:terminals/kitty
* Debian: https://packages.debian.org/buster/kitty
=== macOS packages
kitty is available as a macOS `dmg` file for easy installation from the
link:../../releases[releases page]. You can also run kitty directly from
source using the above install from source instructions, after installing its
dependencies using http://brew.sh/[brew] or a similar package manager.
== Design philosophy
kitty is designed for power keyboard users. To that end all its controls
work with the keyboard (although it fully supports mouse interactions as
well). Its configuration is a simple, human editable, single file for
easy reproducibility (I like to store config files in source control).
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
easy hackability of the UI). It does not depend on any large and complex
UI toolkit, using only OpenGL for rendering everything.
Finally, kitty is designed from the ground up to support all modern
terminal features, such as unicode, true color, bold/italic fonts, text
formatting, etc. It even extends existing text formatting escape codes,
to add support for features not available elsewhere, such as colored and
styled (curly) underlines. One of the design goals of kitty is to be
easily extensible so that new features can be added in the future with
relatively less effort.
== Tabs and Windows
kitty is capable of running multiple programs organized into tabs and
windows. The top level of organization is the _Tab_. Each tab consists
of one or more _windows_. The windows can be arranged in multiple
different layouts, like windows are organized in a tiling window
manager. The keyboard controls (which are all customizable) for tabs and
windows are:
[[scrolling-shortcuts]]
[options="header"]
=== Scrolling
|===
|Action |Shortcut
|Scroll line up | {sc_scroll_line_up}
|Scroll line down | {sc_scroll_line_down}
|Scroll page up | {sc_scroll_page_up}
|Scroll page down | {sc_scroll_page_down}
|Scroll to top | {sc_scroll_home}
|Scroll to bottom | {sc_scroll_end}
|===
[options="header"]
=== Tabs
|===
|Action |Shortcut
|New tab | {sc_new_tab}
|Close tab | {sc_close_tab}
|Next tab | {sc_next_tab}
|Previous tab | {sc_previous_tab}
|Next layout | {sc_next_layout}
|Move tab forward | {sc_move_tab_forward}
|Move tab backward | {sc_move_tab_backward}
|Set tab title | {sc_set_tab_title}
|===
[options="header"]
=== Windows
|===
|Action |Shortcut
|New window | {sc_new_window}
|New OS window| {sc_new_os_window}
|Close window | {sc_close_window}
|Next window | {sc_next_window}
|Previous window | {sc_previous_window}
|Move window forward | {sc_move_window_forward}
|Move window backward | {sc_move_window_backward}
|Move window to top | {sc_move_window_to_top}
|Focus specific window (windows are counted clockwise from the top left corner) | {sc_first_window}, {sc_second_window} ... {sc_tenth_window}
|===
=== Miscellaneous
|===
|Action |Shortcut
|Increase font size | {sc_increase_font_size}
|Decrease font size | {sc_decrease_font_size}
|Restore font size | {sc_restore_font_size}
|Toggle fullscreen | {sc_toggle_fullscreen}
|Input unicode character | {sc_input_unicode_character}
|Click URL using the keyboard | {sc_run_simple_kitten_text_url_hints}
|Pass current selection to program | {sc_pass_selection_to_program}
|Edit kitty config file | {sc_edit_config_file}
|Open a kitty shell | {sc_kitty_shell}
|Increase background opacity | {sc_set_background_opacity_plus0_1}
|Decrease background opacity | {sc_set_background_opacity_0_1}
|Full background opacity | {sc_set_background_opacity_1}
|Reset background opacity | {sc_set_background_opacity_default}
|===
== Controlling kitty from scripts
You can control kitty from the command line/scripts by sending it messages.
You can tell kitty to open/close/rename tabs and windows. You can even send
arbitrary input/text to any specified window. Messages can be sent using `kitty
@`. Note that you must set `allow_remote_control yes` in your
link:kitty/kitty.conf[kitty.conf] to use this feature. It even works over SSH
connections. This feature is best illustrated with a
link:remote-control.asciidoc[tutorial].
== The scrollback buffer
kitty supports scrolling back to view history, just like most terminals. You
can use either the <<scrolling-shortcuts,keyboard shortcuts>> or the mouse
scroll wheel to do so. However, kitty has an extra, neat feature. Sometimes
you need to explore the scrollback buffer in more detail, maybe search for some
text or refer to it side-by-side while typing in a follow-up command. kitty
allows you to do this by pressing the {sc_show_scrollback} key-combination,
which will open the scrollback buffer in your favorite pager program (which is
`less` by default). Colors and text formatting are preserved. You can explore
the scrollback pager comfortably within the pager.
== Unicode input
You can input unicode characters by name, hex code, recently used and even an editable favorites list.
Press {sc_input_unicode_character} to start the unicode input widget, shown below.
image::screenshots/unicode.png?raw=true[Unicode input widget, showing selection of unicode characters by name]
In Code mode, you enter a unicode character by typing in the hex code for the
character and pressing enter, for example, type in 2716 and press enter to get
✖. You can also choose a character from the list of recently used characters by
typing a leading period and then the two character index and pressing Enter.
In 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 also type
a leading period and the index for the match if you dont like to use arrow
keys.
== Hints
kitty has a _hints mode_ to select and act on arbitrary text snippets currently
visible on the screen. For example, you can press {sc_run_kitten_text_hints}
to choose any URL visible on the screen and then open it using your system
browser.
image::screenshots/hints_mode.png?raw=true[URL hints mode]
Similarly, you can press {sc_run_kitten_text_hints_type_path_program} to
select anything that looks like a path or filename and then insert it into the
terminal, very useful for picking files from the output of a git or ls command and
adding them to the command line for the next command.
The hints kitten is very powerful to see more detailed help on its various
options and modes of operation, use: `kitty +kitten hints --help`.
== Miscellaneous features
* You can also hold down `ctrl+shift` and click on a URL to open it in a browser.
* You can double click to select a word and triple click to select a line.
* You can right click to extend a previous selection
== Layouts
Currently, there are five layouts available,
* Stack -- Only a single maximized window is shown at a time
* Tall -- One window is shown full height on the left, the rest of the windows are shown one below the other on the right
* Fat -- One window is 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
* Horizontal -- All windows are shown side-by-side
* Vertical -- All windows are shown one below the other
You can switch between layouts using 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 link:kitty/kitty.conf[kitty.conf] for examples.
You can resize windows inside layouts. Press {sc_start_resizing_window} to
enter resizing mode and follow the on-screen instructions. In a given window
layout only some operations may be possible for a particular window. For
example, in the Tall layout you can make the first 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 row/column will be resized.
Some layouts take options to control their behavior. For example, the `fat` and `tall`
layouts accept the `bias` option to control how the available space is split up. To specify the
option, in kitty.conf use:
```
enabled_layouts tall:bias=70
```
This will make the tall window occupy `70%` of available width. `bias` can be
any number between 10 and 90.
Writing a new layout only requires about a hundred lines of code, so if there is
some layout you want, take a look at link:kitty/layout.py[layout.py] and submit
a pull request!
== Configuration
kitty is highly customizable, everything from keyboard shortcuts, to painting
frames-per-second. See the heavily commented link:kitty/kitty.conf[default
config file] for an overview of all customization possibilities.
By default kitty looks for a config file in the OS config directories (usually
`~/.config/kitty/kitty.conf` and additionally
`~/Library/Preferences/kitty/kitty.conf` on macOS) but you can pass a specific
path via the `--config` option or use the `KITTY_CONFIG_DIRECTORY` environment
variable. See the help for the `--config` option in `kitty --help` for full
details. You can also dump the current configuration using the `--debug-config`
option.
== Startup Sessions
You can control the tabs, window layout, working directory, startup
programs, etc. by creating a "session" file and using the `--session`
command line flag. For example:
....
# Set the window layout for the current tab
layout tall
# Set the working directory for windows in the current tab
cd ~
# Create a window and run the specified command in it
launch zsh
# Create a window with some environment variables set and run vim in it
launch env FOO=BAR vim
# Set the title for the next window
title Chat with x
launch irssi --profile x
# Create a new tab (the part after new_tab is the optional tab name which will
# be displayed in the tab bar, if omitted, the title of the active window will
# be used instead)
new_tab my tab
cd ~/somewhere
# Set the layouts allowed in this tab
enabled_layouts tall, stack
# Set the current layout
layout stack
launch zsh
# Make the current window the active (focused) window
focus
launch emacs
....
== Protocol Extensions
kitty has a few extensions to the xterm protocol, to enable advanced features,
see link:protocol-extensions.asciidoc[Protocol Extensions].
== Font control
kitty has extremely flexible and powerful font selection features. You can
specify individual families for the regular, bold, italic and bold+italic
fonts. You can even specify specific font families for specific ranges of
unicode characters. This allows precise control over text rendering. It can
come in handy for applications like powerline, without the need to use patched
fonts. See the various font related configuration directives in the
link:kitty/kitty.conf[config file].
== Performance
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
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
with child programs takes place in a separate thread from rendering, to improve
smoothness.
There are two parameters you can tune to adjust the performance. ``repaint_delay``
and ``input_delay``. These control the artificial delays introduced into the
render loop to reduce CPU usage. See the link:kitty/kitty.conf[config file] for details.
See also the ``sync_to_monitor`` option to further decrease latency at the cost
of some link:https://en.wikipedia.org/wiki/Screen_tearing[tearing] while scrolling.
You can generate detailed per-function performance data using
link:https://github.com/gperftools/gperftools[gperftools]. Build kitty with
`make profile` which will create an executable called `kitty-profile`. Run
that and perform the task you want to analyse, for example, scrolling a large
file with `less`. After you quit, function call statistics will be printed to
`stdout` and you can use tools like *kcachegrind* for more detailed analysis.
Here are some CPU usage numbers for the task of scrolling a file continuously in 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 size for all terminals on a
`Intel(R) Core(TM) i7-4820K CPU @ 3.70GHz` CPU with a
`Advanced Micro Devices, Inc. [AMD/ATI] Cape Verde XT [Radeon HD 7770/8760 / R7 250X]` GPU.
|===
| Terminal | CPU usage (X + terminal)
| kitty | 6 - 8%
| xterm | 5 - 7% (but scrolling was extremely janky)
| termite | 10 - 13%
| urxvt | 12 - 14%
| gnome-terminal | 15 - 17%
| konsole | 29 - 31%
|===
As you can see, kitty uses much less CPU than all terminals, except xterm, but
its scrolling "smoothness" is much better than that of xterm (at least to my,
admittedly biased, eyes).
== Note for Linux/macOS packagers
While kitty does use python, it is not a traditional python package, so please do not install it in site-packages.
Instead run,
```
python3 setup.py linux-package
```
This will install kitty into the directory `linux-package`. You can run kitty
with `linux-package/bin/kitty`. All the files needed to run kitty will be in
`linux-package/lib/kitty`. The terminfo file will be installed into
`linux-package/share/terminfo`. Simply copy these files into `/usr` to install
kitty. In other words, `linux-package` is the staging area into which kitty is
installed. You can choose a different staging area, by passing the `--prefix`
argument to `setup.py`.
You should probably split kitty into two packages, `kitty-terminfo` that
installs the terminfo file and `kitty` that installs the main program.
This allows users to install the terminfo file on servers into which they ssh,
without needing to install all of kitty.
You also need `tic` to compile the terminfo files, it is usually found in the development package of `ncurses`
This applies to creating packages for kitty for macOS package managers such as
brew or MacPorts as well.
== Frequently Asked Questions
=== Some special symbols are rendered small/truncated in kitty?
The number of cells a unicode character takes up are controlled by the unicode
standard. All characters are rendered in a single cell unless the unicode
standard says they should be rendered in two cells. When a symbol does not fit,
it will either be rescaled to be smaller or truncated (depending on how much
extra space it needs). This is often different from other terminals which just
let the character overflow into neighboring cells, which is fine if the
neighboring cell is empty, but looks terrible if it is not.
Some programs, like powerline, vim with fancy gutter symbols/status-bar, etc.
misuse unicode characters from the private use area to represent symbols. Often
these symbols are square and should be rendered in two cells. However, since
private use area symbols all have their width set to one in the unicode
standard, kitty renders them either smaller or truncated. The exception is if
these characters are followed by a space or empty cell in which case kitty
makes use of the extra cell to render them in two cells.
=== How do I build kitty.app on macOS?
Install `imagemagick`, `optipng` and `librsvg` using `brew` or similar (needed for the logo generation step).
And run:
```
make app
```
This `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
you might have to rebuild the app.
Note that the released kitty.dmg includes all dependencies, unlike the
`kitty.app` built above and is built automatically by using the `kitty` branch of
link:https://github.com/kovidgoyal/build-calibre[build-calibre] however, that
is designed to run on Linux and is not for the faint of heart.
=== 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
be `xterm-kitty`. vim uses *background color erase* even if the terminfo file
does not contain the `bce` capability. This is a bug in vim. You can work around
it by adding the following to your vimrc:
....
let &t_ut=''
....
See link:https://github.com/kovidgoyal/kitty/blob/master/protocol-extensions.asciidoc#setting-text-styles-colors-in-arbitrary-regions-of-the-screen[here]
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?
This happens because the kitty terminfo files are not available on the server.
You can ssh in using the following command which will automatically copy the
terminfo files to the server:
....
kitty +kitten ssh myserver
....
If for some reason that does not work (typically because the server is using a
very limited shell), you can 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 xterm-kitty | ssh myserver tic -x -o \~/.terminfo /dev/stdin
....
Really, the correct solution for this is to convince the OpenSSH maintainers to
have ssh do this automatically when connecting to a server, so that all
terminals work transparently.
=== How do I change the colors in a running kitty instance?
You can either use the
link:http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands[OSC
terminal escape codes] to set colors or you can enable link:remote-control.asciidoc[remote control]
for kitty and use `kitty @ set-colors --help`.
=== 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
workaround that limitation, kitty will read command line options from the 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`.
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:
`/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 PATH.
=== kitty is not able to use my favorite font?
kitty achieves its stellar performance by caching alpha masks of each rendered
character on the GPU, so that every character needs to be rendered only once.
This means it is a strictly character cell based display. As such it can use
only monospace fonts, since every cell in the grid has to be the same size. If
your font is not listed in `kitty list-fonts` it means that it is not
monospace. On Linux you can list all monospace fonts with,
```
fc-list : family spacing | grep spacing=100
```
== Resources on terminal behavior
http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
https://en.wikipedia.org/wiki/C0_and_C1_control_codes
https://vt100.net/

View File

@@ -3,6 +3,7 @@
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
import sys
import os
def icat(args):
@@ -32,7 +33,12 @@ def launch(args):
def run_kitten(args):
kitten = args[1]
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
run_kitten(kitten)
@@ -57,7 +63,21 @@ entry_points = {
namespaced_entry_points = {k: v for k, v in entry_points.items() if k[0] not in '+@'}
def setup_openssl_environment():
# Workaround for Linux distros that have still failed to get their heads
# out of their asses and implement a common location for SSL certificates.
# It's not that hard people, there exists a wonderful tool called the symlink
# See http://www.mobileread.com/forums/showthread.php?t=256095
if 'SSL_CERT_FILE' not in os.environ and 'SSL_CERT_DIR' not in os.environ:
if os.access('/etc/pki/tls/certs/ca-bundle.crt', os.R_OK):
os.environ['SSL_CERT_FILE'] = '/etc/pki/tls/certs/ca-bundle.crt'
elif os.path.isdir('/etc/ssl/certs'):
os.environ['SSL_CERT_DIR'] = '/etc/ssl/certs'
def main():
if getattr(sys, 'frozen', False) and 'darwin' not in sys.platform.lower():
setup_openssl_environment()
first_arg = '' if len(sys.argv) < 2 else sys.argv[1]
func = entry_points.get(first_arg)
if func is None:

20
docs/Makefile Normal file
View File

@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS = -T $(FAIL_WARN)
SPHINXBUILD = python3 .. +launch $(shell which sphinx-build)
SPHINXPROJ = kitty
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

120
docs/_static/custom.css vendored Normal file
View File

@@ -0,0 +1,120 @@
/*
* custom.css
* Copyright (C) 2018 Kovid Goyal
*
* Distributed under terms of the MIT license.
*/
.float-left-img {
float: left;
margin-right: 1em;
margin-bottom: 1em;
}
.float-right-img { float: right; margin-left: 1em; margin-bottom: 1em }
.half-with-img { max-width: 50% }
.fit-img { max-width: 95% }
div.body p, div.body dd, div.body li, div.body blockquote {
text-align: justify;
}
pre {
white-space: pre-wrap;
}
pre.pre {
white-space: pre;
}
a[href], input[type="submit"] { cursor: pointer; }
img[style] {
/* Used for :scale: images to have them render properly but still popup when clicked */
width: auto !important;
height: auto !important;
}
a {
text-decoration: none !important;
border-bottom: none !important;
}
body div.document {
margin-top: 1ex;
}
.major-features li {
margin-top: 0.75ex;
margin-bottom: 0.75ex;
}
.support-form input[type=submit] {
border-radius: 6px;
box-shadow: rgb(255, 246, 175) 0px 1px 0px 0px;
background: linear-gradient(rgb(255, 236, 100) 5%, rgb(255, 171, 35) 100%) rgb(255, 236, 100);
border: 1px solid rgb(255, 170, 34);
display: inline-block;
color: rgb(51, 51, 51);
font-family: Arial;
font-size: 15px;
font-weight: bold;
padding: 6px 24px;
text-decoration: none;
text-shadow: rgb(255, 238, 102) 0px 1px 0px;
}
.support-form input[type=submit]:hover {
background: linear-gradient(rgb(255, 171, 35) 5%, rgb(255, 236, 100) 100%) rgb(255, 171, 35);
}
.support-form input[type=submit]:focus {
outline: 0;
}
div.sphinxsidebar {
font-size: inherit;
line-height: inherit;
}
#sidebartoc li {
margin-top: 0.75ex;
margin-bottom: 0.75ex;
}
#sidebartoc ul {
list-style: none !important;
}
#sidebartoc a[href]:hover {
color: red;
}
.green {
color: green;
}
.cyan {
color: blue;
}
.env {
font-style: italic;
}
.italic {
font-style: italic;
}
.bold {
font-weight: bold;
}
.title {
font-size: larger;
font-weight: bold
}

13
docs/_templates/layout.html vendored Normal file
View File

@@ -0,0 +1,13 @@
{% extends "!layout.html" %}
{%- block extrahead %}
<!-- Google Analytics -->
<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', 'UA-20736318-2', 'auto');
ga('send', 'pageview');
</script>
<script async="async" src='https://www.google-analytics.com/analytics.js'></script>
<!-- End Google Analytics -->
{{ super() }}
{% endblock %}

6
docs/_templates/localtoc.html vendored Normal file
View File

@@ -0,0 +1,6 @@
{%- if display_toc %}
<div>&nbsp;</div>
<div id="sidebartoc">
{{ toc }}
</div>
{%- endif %}

22
docs/_templates/searchbox.html vendored Normal file
View File

@@ -0,0 +1,22 @@
{#
basic/searchbox.html
~~~~~~~~~~~~~~~~~~~~
Sphinx sidebar template: quick search box.
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{%- if pagename != "search" and builder != "singlehtml" %}
<div id="searchbox" style="display: none" role="search">
<div class="searchformwrapper">
<form class="search" action="{{ pathto('search') }}" method="get">
<input type="text" name="q" placeholder="{{ _('Search') }}" />
<input type="submit" value="{{ _('Go') }}" style="cursor: pointer" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
{%- endif %}

7
docs/_templates/support.html vendored Normal file
View File

@@ -0,0 +1,7 @@
{% if pagename != "support" %}
<div id="support" style="text-align: center">
<form class="support-form" action="{{ pathto('support') }}" title="{{ _('Donate to support kitty development') }}">
<input type="submit" value="{{ _('Support kitty') }}">
</form>
</div>
{% endif %}

65
docs/binary.rst Normal file
View File

@@ -0,0 +1,65 @@
kitty - Binary install
========================
.. |ins| replace:: curl -L :literal:`https://sw.kovidgoyal.net/kitty/installer.sh` | sh /dev/stdin
.. highlight:: sh
You can install pre-built binaries of |kitty| if you are on macOS or Linux using
the following simple command:
.. parsed-literal::
:class: pre
|ins|
The binaries will be installed in the standard location for your OS,
:file:`/Applications/kitty.app` on macOS and :file:`~/.local/kitty.app` on
Linux. The installer only touches files in that directory.
Manually installing
---------------------
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
<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
and extract it into a directory. The |kitty| executable will be in the
:file:`bin` sub-directory.
Customizing the installation
--------------------------------
* You can specify a different install location, with ``dest``:
.. parsed-literal::
:class: pre
|ins| \\
dest=/some/other/location
* You can tell the installer not to launch |kitty| after installing it with
``launch=n``:
.. parsed-literal::
:class: pre
|ins| \\
launch=n
* You can use a previously downloaded dmg/tarball, with ``installer``:
.. parsed-literal::
:class: pre
|ins| \\
installer=/path/to/dmg or tarball
Building from source
------------------------
|kitty| is easy to build from source, follow the :doc:`instructions <build>`.

91
docs/build.rst Normal file
View File

@@ -0,0 +1,91 @@
Building kitty from source
==============================
.. image:: https://travis-ci.org/kovidgoyal/kitty.svg?branch=master
:alt: Build status
:target: https://travis-ci.org/kovidgoyal/kitty
|kitty| is designed to run from source, for easy hackability. Make sure
the following dependencies are installed first.
Dependencies
----------------
Run-time dependencies:
* python >= 3.5
* harfbuzz >= 1.5.0
* zlib
* libpng
* freetype (not needed on macOS)
* fontconfig (not needed on macOS)
* ImageMagick (optional, needed to use the ``kitty icat`` tool to display images in the terminal)
* pygments (optional, need for syntax highlighting in ``kitty +kitten diff``)
Build-time dependencies:
* gcc or clang
* pkg-config
* For building on Linux in addition to the above dependencies you might also need to install the ``-dev`` packages for:
``libxcursor-dev``, ``libxrandr-dev``, ``libxi-dev``, ``libxinerama-dev``, ``libgl1-mesa-dev`` and ``libxkbcommon-x11-dev``,
if they are not already installed by your distro.
Install and run from source
------------------------------
.. code-block:: sh
git clone https://github.com/kovidgoyal/kitty && cd kitty
Now build the native code parts of |kitty| with the following command::
make
You can run |kitty|, as::
python3 .
If that works, you can create a script to launch |kitty|:
.. code-block:: sh
#!/usr/bin/env python3
import runpy
runpy.run_path('/path/to/kitty/dir', run_name='__main__')
And place it in :file:`~/bin` or :file:`/usr/bin` so that you can run |kitty| using
just ``kitty``.
Note for Linux/macOS packagers
----------------------------------
While kitty does use python, it is not a traditional python package, so please
do not install it in site-packages.
Instead run::
python3 setup.py linux-package
This will install kitty into the directory :file:`linux-package`. You can run kitty
with :file:`linux-package/bin/kitty`. All the files needed to run kitty will be in
:file:`linux-package/lib/kitty`. The terminfo file will be installed into
:file:`linux-package/share/terminfo`. Simply copy these files into :file:`/usr` to install
kitty. In other words, :file:`linux-package` is the staging area into which kitty is
installed. You can choose a different staging area, by passing the ``--prefix``
argument to :file:`setup.py`.
You should probably split kitty into two packages, :file:`kitty-terminfo` that
installs the terminfo file and :file:`kitty` that installs the main program.
This allows users to install the terminfo file on servers into which they ssh,
without needing to install all of kitty.
.. note::
You need a couple of extra dependencies to build linux-package.
:file:`tic` to compile terminfo files, usually found in the
development package of :file:`ncurses`. Also, if you are building form
a git checkout instead of the released source code tarball, you will
need :file:`sphinx-build` from the `Sphinx documentation generator
<http://www.sphinx-doc.org/>`_.
This applies to creating packages for kitty for macOS package managers such as
brew or MacPorts as well.

679
docs/changelog.rst Normal file
View File

@@ -0,0 +1,679 @@
Changelog
==============
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
0.11.1 [2018-06-17]
------------------------------
- diff kitten: Implement searching for text in the diff (:iss:`574`)
- Add an option :opt:`startup_session` to :file:`kitty.conf` to specify a
default startup session (:iss:`641`)
- Add a command line option :option:`kitty --wait-for-single-instance-window-close`
to make :option:`kitty --single-instance` wait for the closing of the newly opened
window before quitting (:iss:`630`)
- diff kitten: Allow theming the selection background/foreground as well
- diff kitten: Display CRLF line endings using the unicode return symbol
instead of <d> as it is less intrusive (:iss:`638`)
- diff kitten: Fix default foreground/background colors not being restored when
kitten quits (:iss:`637`)
- Fix :option:`kitty @ set-colors --all` not working when more than one window
present (:iss:`632`)
- Fix a regression that broke the legacy increase/decrease_font_size actions
- Clear scrollback on reset (:iss:`631`)
0.11.0 [2018-06-12]
------------------------------
- A new tab bar style "fade" in which each tab's edges fade into the background.
See :opt:`tab_bar_style` and :opt:`tab_fade` for details. The old look can be
restored by setting :opt:`tab_bar_style` to :code:`separator`.
- :doc:`Pre-compiled binaries <binary>` with all bundled dependencies for Linux
(:iss:`595`)
- A :doc:`new kitten <kittens/panel>` to create dock panels on X11 desktops
showing the output from arbitrary terminal programs.
- Reduce data sent to the GPU per render by 30% (:commit:`8dea5b3`)
- Implement changing the font size for individual top level (OS) windows
(:iss:`408`)
- When viewing the scrollback in less using :sc:`show_scrollback` and kitty
is currently scrolled, position the scrollback in less to match kitty's
scroll position. (:iss:`148`)
- ssh kitten: Support all SSH options. It can now be aliased directly to ssh
for convenience. (:pull:`591`)
- icat kitten: Add :option:`kitty +kitten icat --print-window-size` to easily
detect the window size in pixels from scripting languages (:iss:`581`)
- hints kitten: Allow selecting hashes from the terminal with
:sc:`insert_selected_hash` useful for git commits. (:pull:`604`)
- Allow specifying initial window size in number of cells in addition to pixels
(:iss:`436`)
- Add a setting to control the margins to the left and right of the tab-bar
(:iss:`584`)
- When closing a tab switch to the last active tab instead of the right-most
tab (:iss:`585`)
- Wayland: Fix kitty not starting when using wl_roots based compositors
(:iss:`157`)
- Wayland: Fix mouse wheel/touchpad scrolling in opposite direction to other apps (:iss:`594`)
- macOS: Fix the new OS window keyboard shortcut (:sc:`new_os_window`) not
working if no kitty window currently has focus. (:iss:`524`)
- macOS: Keep kitty running even when the last window is closed. This is in
line with how applications are supposed to behave on macOS (:iss:`543`).
There is a new option (:opt:`macos_quit_when_last_window_closed`) to control
this.
- macOS: Add macOS standard shortcuts for copy, paste and new OS window
(⌘+C, ⌘+V, ⌘+N)
- Add a config option (:opt:`editor`) to set the EDITOR kitty uses (:iss:`580`)
- Add a config option (:opt:`x11_hide_window_decorations`) to hide window
decorations under X11/Wayland (:iss:`607`)
- Add an option to @set-window-title to make the title change non-permanent
(:iss:`592`)
- Add support for the CSI t escape code to query window and cell sizes
(:iss:`581`)
- Linux: When using layouts that map the keys to non-ascii characters,
map shortcuts using the ascii equivalents, from the default layout.
(:iss:`606`)
- Linux: Fix fonts not being correctly read from TrueType Collection
(.ttc) files (:iss:`577`)
- Fix :opt:`inactive_text_alpha` also applying to the tab bar (:iss:`612`)
- :doc:`hints kitten <kittens/hints>`: Fix a regression that caused some blank lines to be not
be displayed.
- Linux: Include a man page and the HTML docs when building the linux-package
- Remote control: Fix kitty @ sometimes failing to read the response from
kitty. (:iss:`614`)
- Fix `kitty @ set-colors` not working with the window border colors.
(:iss:`623`)
- Fix a regression in 0.10 that caused incorrect rendering of the status bar in
irssi when used inside screen. (:iss:`621`)
0.10.1 [2018-05-24]
------------------------------
- Add a kitten to easily ssh into servers that automatically copies the
terminfo files over. ``kitty +kitten ssh myserver``.
- diff kitten: Make the keyboard shortcuts configurable (:iss:`563`)
- Allow controlling *background_opacity* via either keyboard shortcuts or
remote control. Note that you must set *dynamic_background_opacity yes* in
kitty.conf first. (:iss:`569`)
- diff kitten: Add keybindings to scroll by page
- diff kitten: Fix incorrect syntax highlighting for a few file formats such as
yaml
- macOS: Fix regression that caused the *macos_option_as_alt* setting to always
be disabled for all OS windows in a kitty instance after the first window
(:iss:`571`)
- Fix Ctrl+Alt+Space not working in normal and application keyboard modes
(:iss:`562`)
0.10.0 [2018-05-21]
------------------------------
- A diff kitten to show side-by-side diffs with syntax highlighting and support
for images. See :doc:`diff kitten <kittens/diff>`.
- Make windows in the various kitty layouts manually resizable. See
:ref:`layouts` for details.
- Implement support for the SGR *faint* escape code to make text blend
into the background (:iss:`446`).
- Make the hints kitten a little smarter (:commit:`ad1109b`)
so that URLs that stretch over multiple lines are detected. Also improve
detection of surrounding brackets/quotes.
- Make the kitty window id available as the environment variable
``KITTY_WINDOW_ID`` (:iss:`532`).
- Add a "fat" layout that is similar to the "tall" layout but vertically
oriented.
- Expand environment variables in config file include directives
- Allow programs running in kitty to read/write from the clipboard (:commit:`889ca77`).
By default only writing is allowed. This feature is supported in many
terminals, search for `OSC 52 clipboard` to find out more about using it.
- Fix moving cursor outside a defined page area incorrectly causing the cursor
to be placed inside the page area. Caused incorrect rendering in neovim, in
some situations (:iss:`542`).
- Render a couple more powerline symbols directly, bypassing the font
(:iss:`550`).
- Fix ctrl+alt+<special> not working in normal and application keyboard (:iss:`548`).
- Partial fix for rendering Right-to-left languages like Arabic. Rendering of
Arabic is never going to be perfect, but now it is at least readable.
- Fix Ctrl+backspace acting as plain backspace in normal and application
keyboard modes (:iss:`538`).
- Have the paste_from_selection action paste from the clipboard on platforms
that do not have a primary selection such as Wayland and macOS
(:iss:`529`)
- Fix cursor_stop_blinking_after=0 not working (:iss:`530`)
0.9.1 [2018-05-05]
------------------------------
- Show a bell symbol on the tab if a bell occurs in one of the windows in the tab and
the window is not the currently focused window
- Change the window border color if a bell occurs in an unfocused window. Can
be disabled by setting the bell_border_color to be the same as the
inactive_border_color.
- macOS: Add support for dead keys
- Unicode input: When searching by name search for prefix matches as well as
whole word matches
- Dynamically allocate the memory used for the scrollback history buffer.
Reduces startup memory consumption when using very large scrollback
buffer sizes.
- Add an option to not request window attention on bell.
- Remote control: Allow matching windows by number (visible position).
- macOS: Fix changing tab title and kitty shell not working
- When triple-clicking select all wrapped lines belonging to a single logical line.
- hints kitten: Detect bracketed URLs and dont include the closing bracket in the URL.
- When calling pass_selection_to_program use the current directory of the child
process as the cwd of the program.
- Add macos_hide_from_tasks option to hide kitty from the macOS task switcher
- macOS: When the macos_titlebar_color is set to background change the titlebar
colors to match the current background color of the active kitty window
- Add a setting to clear all shortcuts defined up to that point in the config
file(s)
- Add a setting (kitty_mod) to change the modifier used by all the default
kitty shortcuts, globally
- Fix Shift+function key not working
- Support the F13 to F25 function keys
- Don't fail to start if the user deletes the hintstyle key from their
fontconfig configuration.
- When rendering a private use unicode codepoint and a space as a two cell
ligature, set the foreground colors of the space cell to match the colors of
the first cell. Works around applications like powerline that use different
colors for the two cells.
- Fix passing @text to other programs such as when viewing the scrollback
buffer not working correctly if kitty is itself scrolled up.
- Fix window focus gained/lost events not being reported to child programs when
switching windows/tabs using the various keyboard shortcuts.
- Fix tab title not changing to reflect the window title when switching between different windows in a tab
- Ignore -e if it is specified on the command line. This is for compatibility
with broken software that assumes terminals should run with an -e option to
execute commands instead of just passing the commands as arguments.
0.9.0 [2018-04-15]
------------------------------
- A new kitty command shell to allow controlling kitty via commands. Press
`ctrl+shift+escape` to run the shell.
- The hints kitten has become much more powerful. Now in addition to URLs you
can use it to select word, paths, filenames, lines, etc. from the screen.
These can be inserted into the terminal, copied to clipboard or sent to
external programs.
- Linux: Switch to libxkbcommon for keyboard handling. It allows kitty to
support XCompose and dead keys and also react to keyboard remapping/layout
change without needing a restart.
- Add support for multiple-key-sequence shortcuts
- A new remote control command `set-colors` to change the current and/or
configured colors.
- When double-clicking to select a word, select words that continue onto the
next/prev line as well.
- Add an `include` directive for the config files to read multiple config files
- Improve mouse selection for windows with padding. Moving the mouse into the
padding area now acts as if the mouse is over the nearest cell.
- Allow setting all 256 terminal colors in the config file
- Fix using `kitty --single-instance` to open a new window in a running kitty
instance, not respecting the `--directory` flag
- URL hints: Exclude trailing punctuation from URLs
- URL hints: Launch the browser from the kitty parent process rather than the
hints kitten. Fixes launching on some systems where xdg-open doesn't like
being run from a kitten.
- Allow using rectangle select mode by pressing shift in addition to the
rectangle select modifiers even when the terminal program has grabbed the
mouse.
0.8.4 [2018-03-31]
-----------------------------
- Fix presence of XDG_CONFIG_DIRS and absence of XDG_CONFIG_HOME preventing
kitty from starting
- Revert change in last release to cell width calculation. Instead just clip
the right edges of characters that overflow the cell by at most two pixels
0.8.3 [2018-03-29]
-----------------------------
- Fix a regression that broke the visual bell and invert screen colors escape
code
- Allow double-click and triple-click + drag to extend selections word at a
time or line at a time
- Add a keyboard shortcut to set the tab title
- Fix setting window title to empty via OSC escape code not working correctly
- Linux: Fix cell width calculation incorrect for some fonts (cell widths are
now calculated by actually rendering bitmaps, which is slower but more
accurate)
- Allow specifying a system wide kitty config file, for all users
- Add a --debug-config command line flag to output data about the system and
kitty configuration.
- Wayland: Fix auto-repeat of keys not working
0.8.2 [2018-03-17]
-----------------------------
- Allow extending existing selections by right clicking
- Add a configurable keyboard shortcut and remote command to set the font size to a specific value
- Add an option to have kitty close the window when the main processes running in it exits, even if there are still background processes writing to that terminal
- Add configurable keyboard shortcuts to switch to a specific layout
- Add a keyboard shortcut to edit the kitty config file easily
- macOS: Fix restoring of window size not correct on Retina screens
- macOS: Add a facility to specify command line arguments when running kitty from the GUI
- Add a focus-tab remote command
- Fix screen not being refreshed immediately after moving a window.
- Fix a crash when getting the contents of the scrollback buffer as text
0.8.1 [2018-03-09]
-----------------------------
- Extend kitty's remote control feature to work over both UNIX and TCP sockets,
so now you can control kitty from across the internet, if you want to.
- Render private use unicode characters that are followed by a space as a two
character ligature. This fixes rendering for applications that misuse
private-use characters to display square symbols.
- Fix Unicode emoji presentation variant selector causing new a fallback font
instance to be created
- Fix a rare error that prevented the Unicode input kitten from working
sometimes
- Allow using Ctrl+Alt+letter in legacy keyboard modes by outputting them as Ctrl+letter and Alt+letter.
This matches other terminals' behavior.
- Fix cursor position off-by-one on horizontal axis when resizing the terminal
- Wayland: Fix auto-repeat of keys not working
- Wayland: Add support for window decorations provided by the Wayland shell
- macOS: Fix URL hints not working
- macOS: Fix shell not starting in login mode on some computers
- macOS: Output errors into console.app when running as a bundle
0.8.0 [2018-02-24]
-----------------------------
- A framework for kittens, that is, small terminal programs designed to run
inside kitty and extend its capabilities. Examples include unicode input and
selecting URLs with the keyboard.
- Input arbitrary unicode characters by pressing Ctrl+Shift+u. You can choose
characters by name, by hex code, by recently used, etc. There is even and
editable Favorites list.
- Open URLs using only the keyboard. kitty has a new "hints mode". Press
Ctrl+Shift+e and all detected URLs on the screen are highlighted with a key
to press to open them. The facility is customizable so you can change
what is detected as a URL and which program is used to open it.
- Add an option to change the titlebar color of kitty windows on macOS
- Only consider Emoji characters with default Emoji presentation to be two
cells wide. This matches the standard. Also add support for the Unicode Emoji
variation presentation selector.
- Prevent video tearing during high speed scrolling by syncing draws
to the monitor's refresh rate. There is a new configuration option to
control this ``sync_to_monitor``.
- When displaying only a single window, use the default background color of the
window (which can be changed via escape codes) as the color for the margin
and padding of the window.
- Add some non standard terminfo capabilities used by neovim and tmux.
- Fix large drop in performance when using multiple top-level windows on macOS
- Fix save/restore of window sizes not working correctly.
- Remove option to use system wcwidth(). Now always use a wcwidth() based on
the Unicode standard. Only sane way.
- Fix a regression that caused a few ligature glyphs to not render correctly in
rare circumstances.
- Browsing the scrollback buffer now happens in an overlay window instead of a
new window/tab.
0.7.1 [2018-01-31]
---------------------------
- Add an option to adjust the width of character cells
- Fix selecting text with the mouse in the scrollback buffer selecting text
from the line above the actually selected line
- Fix some italic fonts having the right edge of characters cut-off,
unnecessarily
0.7.0 [2018-01-24]
---------------------------
- Allow controlling kitty from the shell prompt/scripts. You can
open/close/rename windows and tabs and even send input to specific windows.
See the README for details.
- Add option to put tab bar at the top instead of the bottom
- Add option to override the default shell
- Add "Horizontal" and "Vertical" window layouts
- Sessions: Allow setting titles and working directories for individual windows
- Option to copy to clipboard on mouse select
- Fix incorrect reporting of mouse move events when using the SGR protocol
- Make alt+backspace delete the previous word
- Take the mouse wheel multiplier option in to account when generating fake key
scroll events
- macOS: Fix closing top-level window does not transfer focus to other
top-level windows.
- macOS: Fix alt+arrow keys not working when disabling the macos_option_as_alt
config option.
- kitty icat: Workaround for bug in ImageMagick that would cause some images
to fail to display at certain sizes.
- Fix rendering of text with ligature fonts that do not use dummy glyphs
- Fix a regression that caused copying of the selection to clipboard to only
copy the visible part of the selection
- Fix incorrect handling of some unicode combining marks that are not re-ordered
- Fix handling on non-BMP combining characters
- Drop the dependency on libunistring
0.6.1 [2017-12-28]
---------------------------
- Add an option to fade the text in inactive windows
- Add new actions to open windows/tabs/etc. with the working directory set to
the working directory of the current window.
- Automatically adjust cell size when DPI changes, for example when kitty is
moved from one monitor to another with a different DPI
- Ensure underlines are rendered even for fonts with very poor metrics
- Fix some emoji glyphs not colored on Linux
- Internal wcwidth() implementation is now auto-generated from the unicode
standard database
- Allow configuring the modifiers to use for rectangular selection with the
mouse.
- Fix incorrect minimum wayland version in the build script
- Fix a crash when detecting a URL that ends at the end of the line
- Fix regression that broke drawing of hollow cursor when window loses focus
0.6.0 [2017-12-18]
---------------------------
- Support background transparency via the background_opacity option. Provided
that your OS/window manager supports transparency, you can now have kitty
render pixels that have only the default background color as
semi-transparent.
- Support multiple top level (OS) windows. These windows all share the sprite
texture cache on the GPU, further reducing overall resource usage. Use
the shortcut `ctrl+shift+n` to open a new top-level window.
- Add support for a *daemon* mode using the `--single-instance` command line
option. With this option you can have only a single kitty instance running.
All future invocations simply open new top-level windows in the existing
instance.
- Support colored emoji
- Use CoreText instead of FreeType to render text on macOS
- Support running on the "low power" GPU on dual GPU macOS machines
- Add a new "grid" window layout
- Drop the dependency on glfw (kitty now uses a modified, bundled copy of glfw)
- Add an option to control the audio bell volume on X11 systems
- Add a command line switch to set the name part of the WM_CLASS window
property independently.
- Add a command line switch to set the window title.
- Add more options to customize the tab-bar's appearance (font styles and
separator)
- Allow drag and drop of files into kitty. On drop kitty will paste the
file path to the running program.
- Add an option to control the underline style for URL highlighting on hover
- X11: Set the WINDOWID environment variable
- Fix middle and right buttons swapped when sending mouse events to child
processes
- Allow selecting in a rectangle by holding down Ctrl+Alt while dragging with
the mouse.
0.5.1 [2017-12-01]
---------------------------
- Add an option to control the thickness of lines in box drawing characters
- Increase max. allowed ligature length to nine characters
- Fix text not vertically centered when adjusting line height
- Fix unicode block characters not being rendered properly
- Fix shift+up/down not generating correct escape codes
- Image display: Fix displaying images taller than two screen heights not
scrolling properly
0.5.0 [2017-11-19]
---------------------------
- Add support for ligature fonts such as Fira Code, Hasklig, etc. kitty now
uses harfbuzz for text shaping which allow it to support advanced OpenType
features such as contextual alternates/ligatures/combining glyphs/etc.
- Make it easy to select fonts by allowing listing of monospace fonts using:
kitty list-fonts
- Add an option to have window focus follow mouse
- Add a keyboard shortcut (ctrl+shift+f11) to toggle fullscreen mode
- macOS: Fix handling of option key. It now behaves just like the alt key on
Linux. There is an option to make it type unicode characters instead.
- Linux: Add support for startup notification on X11 desktops. kitty will
now inform the window manager when its startup is complete.
- Fix shell prompt being duplicated when window is resized
- Fix crash when displaying more than 64 images in the same session
- Add support for colons in SGR color codes. These are generated by some
applications such as neovim when they mistakenly identify kitty as a libvte
based terminal.
- Fix mouse interaction not working in apps using obsolete mouse interaction
protocols
- Linux: no longer require glew as a dependency
0.4.2 [2017-10-23]
---------------------------
- Fix a regression in 0.4.0 that broke custom key mappings
- Fix a regression in 0.4.0 that broke support for non-QWERTY keyboard layouts
- Avoid using threads to reap zombie child processes. Also prevent kitty from
hanging if the open program hangs when clicking on a URL.
0.4.0 [2017-10-22]
---------------------------
- Support for drawing arbitrary raster graphics (images) in the terminal via a
new graphics protocol. kitty can draw images with full 32-bit color using both
ssh connections and files/shared memory (when available) for better
performance. The drawing primitives support alpha blending and z-index.
Images can be drawn both above and below text. See :doc:`graphics-protocol`.
for details.
- Refactor kitty's internals to make it even faster and more efficient. The CPU
usage of kitty + X server while doing intensive tasks such as scrolling a
file continuously in less has been reduced by 50%. There are now two
configuration options ``repaint_delay`` and ``input_delay`` you can use to
fine tune kitty's performance vs CPU usage profile. The CPU usage of kitty +
X when scrolling in less is now significantly better than most (all?) other
terminals. See :doc:`performance`.
- Hovering over URLs with the mouse now underlines them to indicate they
can be clicked. Hold down Ctrl+Shift while clicking to open the URL.
- Selection using the mouse is now more intelligent. It does not add
blank cells (i.e. cells that have no content) after the end of text in a
line to the selection.
- The block cursor in now fully opaque but renders the character under it in
the background color, for enhanced visibility.
- Allow combining multiple independent actions into a single shortcut
- Add a new shortcut to pass the current selection to an external program
- Allow creating shortcuts to open new windows running arbitrary commands. You
can also pass the current selection to the command as an arguments and the
contents of the screen + scrollback buffer as stdin to the command.

536
docs/conf.py Normal file
View File

@@ -0,0 +1,536 @@
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
import os
import re
import subprocess
import sys
from functools import partial
from docutils import nodes
from docutils.parsers.rst.roles import set_classes
from pygments.lexer import RegexLexer, bygroups
from pygments.token import (
Comment, Keyword, Literal, Name, Number, String, Whitespace
)
from sphinx import addnodes
from sphinx.util.logging import getLogger
from kitty.constants import str_version
# config {{{
# -- Project information -----------------------------------------------------
project = 'kitty'
copyright = '2018, Kovid Goyal'
author = 'Kovid Goyal'
building_man_pages = 'man' in sys.argv
# The short X.Y version
version = str_version
# The full version, including alpha/beta/rc tags
release = str_version
logger = getLogger(__name__)
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
needs_sphinx = '1.7'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.ifconfig',
'sphinx.ext.viewcode',
'sphinx.ext.githubpages',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = [
'_build', 'Thumbs.db', '.DS_Store',
'generated/cli-*.rst', 'generated/conf-*.rst'
]
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
rst_prolog = '''
.. |kitty| replace:: *kitty*
.. role:: green
.. role:: italic
.. role:: bold
.. role:: cyan
.. role:: title
.. role:: env
'''
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# 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
# documentation.
#
html_theme_options = {
'logo': 'kitty.png',
'show_powered_by': False,
'fixed_sidebar': True,
'sidebar_collapse': True,
'github_button': False,
'github_banner': True,
'github_user': 'kovidgoyal',
'github_repo': 'kitty',
# increase contrast of link color with text color
'link': '#00587d',
'link_hover': 'green',
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static', '../logo/kitty.png']
html_context = {
'css_files': ['_static/custom.css']
}
html_favicon = '../logo/kitty.png'
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
html_sidebars = {
'**': [
'about.html',
'support.html',
'searchbox.html',
'localtoc.html',
'relations.html',
]
}
html_show_sourcelink = False
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('invocation', 'kitty', 'kitty Documentation',
[author], 1)
]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'kitty', 'kitty Documentation',
author, 'kitty', 'A cross-platform, fast, feature full, GPU based terminal emulator',
'Miscellaneous'),
]
# }}}
# GitHub linking inlne roles {{{
def num_role(which, name, rawtext, text, lineno, inliner, options={}, content=[]):
' Link to a github issue '
try:
issue_num = int(text)
if issue_num <= 0:
raise ValueError
except ValueError:
msg = inliner.reporter.error(
'GitHub issue number must be a number greater than or equal to 1; '
'"%s" is invalid.' % text, line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
url = f'https://github.com/kovidgoyal/kitty/{which}/{issue_num}'
set_classes(options)
node = nodes.reference(rawtext, f'#{issue_num}', refuri=url, **options)
return [node], []
def commit_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
' Link to a github commit '
try:
commit_id = subprocess.check_output(f'git rev-list --max-count=1 --skip=# {text}'.split()).decode('utf-8').strip()
except Exception:
msg = inliner.reporter.error(
f'GitHub commit id "{text}" not recognized.', line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
url = f'https://github.com/kovidgoyal/kitty/commit/{commit_id}'
set_classes(options)
short_id = subprocess.check_output(f'git rev-list --max-count=1 --abbrev-commit --skip=# {commit_id}'.split()).decode('utf-8').strip()
node = nodes.reference(rawtext, f'commit: {short_id}', refuri=url, **options)
return [node], []
# }}}
# Sidebar ToC {{{
def create_toc(app, pagename):
toctree = app.env.get_toc_for(pagename, app.builder)
if toctree is not None:
subtree = toctree[toctree.first_child_matching_class(nodes.list_item)]
bl = subtree.first_child_matching_class(nodes.bullet_list)
if bl is None:
return # Empty ToC
subtree = subtree[bl]
# for li in subtree.traverse(nodes.list_item):
# modify_li(li)
# subtree['ids'] = [ID]
return app.builder.render_partial(subtree)['fragment']
def add_html_context(app, pagename, templatename, context, *args):
if 'toc' in context:
context['toc'] = create_toc(app, pagename) or context['toc']
# }}}
# CLI docs {{{
def write_cli_docs(all_kitten_names):
from kitty.cli import option_spec_as_rst
with open('generated/cli-kitty.rst', 'w') as f:
f.write(option_spec_as_rst(appname='kitty').replace(
'kitty --to', 'kitty @ --to'))
as_rst = partial(option_spec_as_rst, heading_char='_')
from kitty.remote_control import global_options_spec, cli_msg, cmap, all_commands
with open('generated/cli-kitty-at.rst', 'w') as f:
p = partial(print, file=f)
p('kitty @\n' + '-' * 80)
p('.. program::', 'kitty @')
p('\n\n' + as_rst(
global_options_spec, message=cli_msg, usage='command ...', appname='kitty @'))
from kitty.cmds import cli_params_for
for cmd_name in all_commands:
func = cmap[cmd_name]
p(f'.. _at_{func.name}:\n')
p('kitty @', func.name + '\n' + '-' * 120)
p('.. program::', 'kitty @', func.name)
p('\n\n' + as_rst(*cli_params_for(func)))
from kittens.runner import get_kitten_cli_docs
for kitten in all_kitten_names:
data = get_kitten_cli_docs(kitten)
if data:
with open(f'generated/cli-kitten-{kitten}.rst', 'w') as f:
p = partial(print, file=f)
p('.. program::', f'kitty +kitten {kitten}')
p('\n\n' + option_spec_as_rst(
data['options'], message=data['help_text'], usage=data['usage'], appname=f'kitty +kitten {kitten}',
heading_char='^'))
# }}}
# config file docs {{{
class ConfLexer(RegexLexer):
name = 'Conf'
aliases = ['conf']
filenames = ['*.conf']
tokens = {
'root': [
(r'#.*?$', Comment.Single),
(r'\s+$', Whitespace),
(r'\s+', Whitespace),
(r'(include)(\s+)(.+?)$', bygroups(Comment.Preproc, Whitespace, Name.Namespace)),
(r'(map)(\s+)(\S+)(\s+)', bygroups(
Keyword.Declaration, Whitespace, String, Whitespace), 'action'),
(r'(symbol_map)(\s+)(\S+)(\s+)(.+?)$', bygroups(
Keyword.Declaration, Whitespace, String, Whitespace, Literal)),
(r'([a-zA-Z_0-9]+)(\s+)', bygroups(
Name.Variable, Whitespace), 'args'),
],
'action': [
(r'[a-z_0-9]+$', Name.Function, 'root'),
(r'[a-z_0-9]+', Name.Function, 'args'),
],
'args': [
(r'\s+', Whitespace, 'args'),
(r'\b(yes|no)\b$', Number.Bin, 'root'),
(r'\b(yes|no)\b', Number.Bin, 'args'),
(r'[+-]?[0-9]+\s*$', Number.Integer, 'root'),
(r'[+-]?[0-9.]+\s*$', Number.Float, 'root'),
(r'[+-]?[0-9]+', Number.Integer, 'args'),
(r'[+-]?[0-9.]+', Number.Float, 'args'),
(r'#[a-fA-F0-9]{3,6}\s*$', String, 'root'),
(r'#[a-fA-F0-9]{3,6}\s*', String, 'args'),
(r'.+', String, 'root'),
],
}
class SessionLexer(RegexLexer):
name = 'Session'
aliases = ['session']
filenames = ['*.session']
tokens = {
'root': [
(r'#.*?$', Comment.Single),
(r'[a-z][a-z0-9_]+', Name.Function, 'args'),
],
'args': [
(r'.*?$', Literal, 'root'),
]
}
def link_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
m = re.match(r'(.+)\s+<(.+?)>', text)
if m is None:
msg = inliner.reporter.error(f'link "{text}" not recognized', line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]
text, url = m.group(1, 2)
set_classes(options)
node = nodes.reference(rawtext, text, refuri=url, **options)
return [node], []
def expand_opt_references(conf_name, text):
conf_name += '.'
def expand(m):
ref = m.group(1)
if '<' not in ref and '.' not in ref:
full_ref = conf_name + ref
return ':opt:`{} <{}>`'.format(ref, full_ref)
return m.group()
return re.sub(r':opt:`(.+?)`', expand, text)
opt_aliases = {}
shortcut_slugs = {}
def parse_opt_node(env, sig, signode):
"""Transform an option description into RST nodes."""
count = 0
firstname = ''
for potential_option in sig.split(', '):
optname = potential_option.strip()
if count:
signode += addnodes.desc_addname(', ', ', ')
text = optname.split('.', 1)[-1]
signode += addnodes.desc_name(text, text)
if not count:
firstname = optname
signode['allnames'] = [optname]
else:
signode['allnames'].append(optname)
opt_aliases[optname] = firstname
count += 1
if not firstname:
raise ValueError('{} is not a valid opt'.format(sig))
return firstname
def parse_shortcut_node(env, sig, signode):
"""Transform a shortcut description into RST nodes."""
conf_name, text = sig.split('.', 1)
signode += addnodes.desc_name(text, text)
return sig
def render_conf(conf_name, all_options):
from kitty.conf.definition import merged_opts, Option
ans = ['.. default-domain:: conf', '']
a = ans.append
current_group = None
all_options = list(all_options)
kitty_mod = 'kitty_mod'
def render_group(group):
a('')
a(f'.. _conf-{conf_name}-{group.name}:')
a('')
a(group.short_text)
heading_level = '+' if '.' in group.name else '^'
a(heading_level * (len(group.short_text) + 20))
a('')
if group.start_text:
a(group.start_text)
a('')
def handle_group_end(group):
if group.end_text:
a(''), a(current_group.end_text)
def handle_group(new_group, new_group_is_shortcut=False):
nonlocal current_group
if new_group is not current_group:
if current_group:
handle_group_end(current_group)
current_group = new_group
render_group(current_group)
def handle_option(i, opt):
nonlocal kitty_mod
if not opt.long_text or not opt.add_to_docs:
return
handle_group(opt.group)
if opt.name == 'kitty_mod':
kitty_mod = opt.defval_as_string
mopts = list(merged_opts(all_options, opt, i))
a('.. opt:: ' + ', '.join(conf_name + '.' + mo.name for mo in mopts))
a('.. code-block:: conf')
a('')
sz = max(len(x.name) for x in mopts)
for mo in mopts:
a((' {:%ds} {}' % sz).format(mo.name, mo.defval_as_string))
a('')
if opt.long_text:
a(expand_opt_references(conf_name, opt.long_text))
a('')
def handle_shortcuts(shortcuts):
sc = shortcuts[0]
handle_group(sc.group, True)
sc_text = f'{conf_name}.{sc.short_text}'
a('.. shortcut:: ' + sc_text)
shortcuts = [s for s in shortcuts if s.add_to_default]
shortcut_slugs[f'{conf_name}.{sc.name}'] = (sc_text, sc.key.replace('kitty_mod', kitty_mod))
if shortcuts:
a('.. code-block:: conf')
a('')
for x in shortcuts:
if x.add_to_default:
a(' map {} {}'.format(x.key.replace('kitty_mod', kitty_mod), x.action_def))
a('')
if sc.long_text:
a(expand_opt_references(conf_name, sc.long_text))
a('')
for i, opt in enumerate(all_options):
if isinstance(opt, Option):
handle_option(i, opt)
else:
handle_shortcuts(opt)
if current_group:
handle_group_end(current_group)
return '\n'.join(ans)
def process_opt_link(env, refnode, has_explicit_title, title, target):
conf_name, opt = target.partition('.')[::2]
if not opt:
conf_name, opt = 'kitty', conf_name
full_name = conf_name + '.' + opt
return title, opt_aliases.get(full_name, full_name)
def process_shortcut_link(env, refnode, has_explicit_title, title, target):
conf_name, slug = target.partition('.')[::2]
if not slug:
conf_name, slug = 'kitty', conf_name
full_name = conf_name + '.' + slug
try:
target, title = shortcut_slugs[full_name]
except KeyError:
logger.warning('Unknown shortcut: {}'.format(target), location=refnode)
return title, target
def write_conf_docs(app, all_kitten_names):
app.add_lexer('conf', ConfLexer())
app.add_object_type(
'opt', 'opt',
indextemplate="pair: %s; Config Setting",
parse_node=parse_opt_node,
)
# Warn about opt references that could not be resolved
opt_role = app.registry.domain_roles['std']['opt']
opt_role.warn_dangling = True
opt_role.process_link = process_opt_link
app.add_object_type(
'shortcut', 'sc',
indextemplate="pair: %s; Keyboard Shortcut",
parse_node=parse_shortcut_node,
)
sc_role = app.registry.domain_roles['std']['sc']
sc_role.warn_dangling = True
sc_role.process_link = process_shortcut_link
def generate_default_config(all_options, name):
from kitty.conf.definition import as_conf_file
with open(f'generated/conf-{name}.rst', 'w', encoding='utf-8') as f:
print('.. highlight:: conf\n', file=f)
f.write(render_conf(name, all_options.values()))
conf_name = re.sub(r'^kitten-', '', name) + '.conf'
with open(f'generated/conf/{conf_name}', 'w', encoding='utf-8') as f:
text = '\n'.join(as_conf_file(all_options.values()))
print(text, file=f)
from kitty.config_data import all_options
generate_default_config(all_options, 'kitty')
from kittens.runner import get_kitten_conf_docs
for kitten in all_kitten_names:
all_options = get_kitten_conf_docs(kitten)
if all_options:
generate_default_config(all_options, f'kitten-{kitten}')
# }}}
def setup(app):
os.makedirs('generated/conf', exist_ok=True)
from kittens.runner import all_kitten_names
all_kitten_names = all_kitten_names()
write_cli_docs(all_kitten_names)
write_conf_docs(app, all_kitten_names)
app.add_lexer('session', SessionLexer())
app.add_role('link', link_role)
app.add_role('iss', partial(num_role, 'issues'))
app.add_role('pull', partial(num_role, 'pull'))
app.add_role('commit', commit_role)
app.connect('html-page-context', add_html_context)

40
docs/conf.rst Normal file
View File

@@ -0,0 +1,40 @@
:tocdepth: 2
Configuring kitty
===============================
.. highlight:: conf
|kitty| is highly customizable, everything from keyboard shortcuts, to painting
frames-per-second. See below for an overview of all customization
possibilities.
You can open the config file within kitty by pressing :sc:`edit_config_file`.
You can also display the current configuration by running ``kitty
--debug-config``.
.. _confloc:
|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
:option:`kitty --config` option or use the ``KITTY_CONFIG_DIRECTORY``
environment variable. See the :option:`kitty --config` option for full details.
You can include secondary config files via the :code:`include` directive. If
you use a relative path for include, it is resolved with respect to the
location of the current config file. Note that environment variables are
expanded, so :code:`${USER}.conf` becomes :file:`name.conf` if
:code:`USER=name`. For example::
include other.conf
.. include:: /generated/conf-kitty.rst
Sample kitty.conf
^^^^^^^^^^^^^^^^^^^^^
You can download a sample :file:`kitty.conf` file with all default settings and
comments describing each setting by clicking: :download:`sample kitty.conf
</generated/conf/kitty.conf>`.

115
docs/faq.rst Normal file
View File

@@ -0,0 +1,115 @@
Frequently Asked Questions
==============================
.. highlight:: sh
.. contents::
Some special symbols are rendered small/truncated in kitty?
-----------------------------------------------------------------
The number of cells a unicode character takes up are controlled by the unicode
standard. All characters are rendered in a single cell unless the unicode
standard says they should be rendered in two cells. When a symbol does not fit,
it will either be rescaled to be smaller or truncated (depending on how much
extra space it needs). This is often different from other terminals which just
let the character overflow into neighboring cells, which is fine if the
neighboring cell is empty, but looks terrible if it is not.
Some programs, like powerline, vim with fancy gutter symbols/status-bar, etc.
misuse unicode characters from the private use area to represent symbols. Often
these symbols are square and should be rendered in two cells. However, since
private use area symbols all have their width set to one in the unicode
standard, |kitty| renders them either smaller or truncated. The exception is if
these characters are followed by a space or empty cell in which case kitty
makes use of the extra cell to render them in two cells.
How do I build kitty.app on macOS?
----------------------------------------
Install `imagemagick`, `optipng` and `librsvg` using `brew` or similar (needed
for the logo generation step). And run::
make app
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
you might have to rebuild the app.
Note that the released :file:`kitty.dmg` includes all dependencies, unlike the
:file:`kitty.app` built above and is built automatically by using the :file:`kitty` branch of
`build-calibre <https://github.com/kovidgoyal/build-calibre>`_ however, that
is designed to run on Linux and is not for the faint of heart.
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
be ``xterm-kitty``. vim uses *background color erase* even if the terminfo file
does not contain the ``bce`` capability. This is a bug in vim. You can work around
it by adding the following to your vimrc::
let &t_ut=''
See :ref:`here <ext_styles>` 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?
-----------------------------------------------------------------------------------------------------------------------
This happens because the |kitty| terminfo files are not available on the server.
You can ssh in using the following command which will automatically copy the
terminfo files to the server::
kitty +kitten ssh myserver
If for some reason that does not work (typically because the server is using a
a non POSIX compliant shell), you can 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 xterm-kitty | ssh myserver tic -x -o \~/.terminfo /dev/stdin
Really, the correct solution for this is to convince the OpenSSH maintainers to
have ssh do this automatically when connecting to a server, so that all
terminals work transparently.
How do I change the colors in a running kitty instance?
------------------------------------------------------------
You can either use the
`OSC terminal escape codes <http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands>`_
to set colors or you can enable :doc:`remote control <remote-control>`
for |kitty| and use :ref:`at_set-colors`.
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
workaround that limitation, |kitty| will read command line options from the file
: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``.
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:
: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 ``PATH``.
kitty is not able to use my favorite font?
---------------------------------------------
|kitty| achieves its stellar performance by caching alpha masks of each rendered
character on the GPU, so that every character needs to be rendered only once.
This means it is a strictly character cell based display. As such it can use
only monospace fonts, since every cell in the grid has to be the same size. If
your font is not listed in ``kitty list-fonts`` it means that it is not
monospace. On Linux you can list all monospace fonts with::
fc-list : family spacing | grep spacing=100

398
docs/graphics-protocol.rst Normal file
View File

@@ -0,0 +1,398 @@
:tocdepth: 3
The terminal graphics protocol
==================================
The goal of this specification is to create a flexible and performant protocol
that allows the program running in the terminal, hereafter called the *client*,
to render arbitrary pixel (raster) graphics to the screen of the terminal
emulator. The major design goals are
* Should not require terminal emulators to understand image formats.
* Should allow specifying graphics to be drawn at individual pixel positions.
* The graphics should integrate with the text, in particular it should be possible to draw graphics
below as well as above the text, with alpha blending. The graphics should also scroll with the text, automatically.
* Should use optimizations when the client is running on the same computer as the terminal emulator.
For some discussion regarding the design choices, see `#33
<https://github.com/kovidgoyal/kitty/issues/33>`_.
To see a quick demo, inside a |kitty| terminal run::
kitty icat path/to/some/image.png
You can also see a screenshot with more sophisticated features such as
alpha-blending and text over graphics.
.. image:: https://user-images.githubusercontent.com/1308621/31647475-1188ab66-b326-11e7-8d26-24b937f1c3e8.png
:alt: Demo of graphics rendering in kitty
:align: center
Some programs that use the kitty graphics protocol:
* `termpdf <https://github.com/dsanson/termpdf>`_ - a terminal PDF/DJVU/CBR viewer
* `ranger <https://github.com/ranger/ranger>`_ - a terminal file manager, with
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
* `neofetch <https://github.com/dylanaraps/neofetch>`_ - A command line system
information tool
.. contents::
Getting the window size
-------------------------
In order to know what size of images to display and how to position them, the
client must be able to get the window size in pixels and the number of cells
per row and column. This can be done by using the ``TIOCGWINSZ`` ioctl. Some
code to demonstrate its use
In C:
.. code-block:: c
struct ttysize ts;
ioctl(0, TIOCGWINSZ, &ts);
printf("number of columns: %i, number of rows: %i, screen width: %i, screen height: %i\n", sz.ws_col, sz.ws_row, sz.ws_xpixel, sz.ws_ypixel);
In Python:
.. code-block:: python
import array, fcntl, termios
buf = array.array('H', [0, 0, 0, 0])
fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, buf)
print('number of columns: {}, number of rows: {}, screen width: {}, screen height: {}'.format(*buf))
Note that some terminals return ``0`` for the width and height values. Such
terminals should be modified to return the correct values. Examples of
terminals that return correct values: ``kitty, xterm``
You can also use the *CSI t* escape code to get the screen size. Send
``<ESC>[14t`` to *stdout* and kitty will reply on *stdin* with
``<ESC>[4;<height>;<width>t`` where *height* and *width* are the window size in
pixels. This escape code is supported in many terminals, not just kitty.
The graphics escape code
---------------------------
All graphics escape codes are of the form::
<ESC>_G<control data>;<payload><ESC>\
This is a so-called *Application Programming Command (APC)*. Most terminal
emulators ignore APC codes, making it safe to use.
The control data is a comma-separated list of ``key=value`` pairs. The payload
is arbitrary binary data, base64-encoded to prevent interoperation problems
with legacy terminals that get confused by control codes within an APC code.
The meaning of the payload is interpreted based on the control data.
The first step is to transmit the actual image data.
Transferring pixel data
--------------------------
The first consideration when transferring data between the client and the
terminal emulator is the format in which to do so. Since there is a vast and
growing number of image formats in existence, it does not make sense to have
every terminal emulator implement support for them. Instead, the client should
send simple pixel data to the terminal emulator. The obvious downside to this
is performance, especially when the client is running on a remote machine.
Techniques for remedying this limitation are discussed later. The terminal
emulator must understand pixel data in three formats, 24-bit RGB, 32-bit RGBA and
PNG. This is specified using the ``f`` key in the control data. ``f=32`` (which is the
default) indicates 32-bit RGBA data and ``f=24`` indicates 24-bit RGB data and ``f=100``
indicates PNG data. The PNG format is supported for convenience and a compact way
of transmitting paletted images.
RGB and RGBA data
~~~~~~~~~~~~~~~~~~~
In these formats the pixel data is stored directly as 3 or 4 bytes per pixel, respectively.
When specifying images in this format, the image dimensions **must** be sent in the control data.
For example::
<ESC>_Gf=24,s=10,v=20;<payload><ESC>\
Here the width and height are specified using the ``s`` and ``v`` keys respectively. Since
``f=24`` there are three bytes per pixel and therefore the pixel data must be ``3 * 10 * 20 = 600``
bytes.
PNG data
~~~~~~~~~~~~~~~
In this format any PNG image can be transmitted directly. For example::
<ESC>_Gf=100;<payload><ESC>\
The PNG format is specified using the ``f=100`` key. The width and height of
the image will be read from the PNG data itself. Note that if you use both PNG and
compression, then you must provide the ``S`` key with the size of the PNG data.
Compression
~~~~~~~~~~~~~
The client can send compressed image data to the terminal emulator, by specifying the
``o`` key. Currently, only zlib based deflate compression is supported, which is specified using
``o=z``. For example::
<ESC>_Gf=24,s=10,v=20,o=z;<payload><ESC>\
This is the same as the example from the RGB data section, except that the
payload is now compressed using deflate. The terminal emulator will decompress
it before rendering. You can specify compression for any format. The terminal
emulator will decompress before interpreting the pixel data.
The transmission medium
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The transmission medium is specified using the ``t`` key. The ``t`` key defaults to ``d``
and can take the values:
================== ============
Value of `t` Meaning
================== ============
``d`` Direct (the data is transmitted within the escape code itself)
``f`` A simple file
``t`` A temporary file, the terminal emulator will delete the file after reading the pixel data
``s`` A `POSIX shared memory object <http://man7.org/linux/man-pages/man7/shm_overview.7.html>`_.
The terminal emulator will delete it after reading the pixel data
================== ============
Local client
^^^^^^^^^^^^^^
First let us consider the local client techniques (files and shared memory). Some examples::
<ESC>_Gf=100,t=f;<encoded /path/to/file.png><ESC>\
Here we tell the terminal emulator to read PNG data from the specified file of
the specified size::
<ESC>_Gs=10,v=2,t=s,o=z;<encoded /some-shared-memory-name><ESC>\
Here we tell the terminal emulator to read compressed image data from
the specified shared memory object.
The client can also specify a size and offset to tell the terminal emulator
to only read a part of the specified file. The is done using the ``S`` and ``O``
keys respectively. For example::
<ESC>_Gs=10,v=2,t=s,S=80,O=10;<encoded /some-shared-memory-name><ESC>\
This tells the terminal emulator to read ``80`` bytes starting from the offset ``10``
inside the specified shared memory buffer.
Remote client
^^^^^^^^^^^^^^^^
Remote clients, those that are unable to use the filesystem/shared memory to
transmit data, must send the pixel data directly using escape codes. Since
escape codes are of limited maximum length, the data will need to be chunked up
for transfer. This is done using the ``m`` key. The pixel data must first be
base64 encoded then chunked up into chunks no larger than ``4096`` bytes. The client
then sends the graphics escape code as usual, with the addition of an ``m`` key that
must have the value ``1`` for all but the last chunk, where it must be ``0``. For example,
if the data is split into three chunks, the client would send the following
sequence of escape codes to the terminal emulator::
<ESC>_Gs=100,v=30,m=1;<encoded pixel data first chunk><ESC>\
<ESC>_Gm=1;<encoded pixel data second chunk><ESC>\
<ESC>_Gm=0;<encoded pixel data last chunk><ESC>\
Note that only the first escape code needs to have the full set of control
codes such as width, height, format etc. Subsequent chunks must have
only the ``m`` key. The client **must** finish sending all chunks for a single image
before sending any other graphics related escape codes.
Detecting available transmission mediums
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Since a client has no a-priori knowledge of whether it shares a filesystem/shared memory
with the terminal emulator, it can send an id with the control data, using the ``i`` key
(which can be an arbitrary positive integer up to 4294967295, it must not be zero).
If it does so, the terminal emulator will reply after trying to load the image, saying
whether loading was successful or not. For example::
<ESC>_Gi=31,s=10,v=2,t=s;<encoded /some-shared-memory-name><ESC>\
to which the terminal emulator will reply (after trying to load the data)::
<ESC>_Gi=31;error message or OK<ESC>\
Here the ``i`` value will be the same as was sent by the client in the original
request. The message data will be a ASCII encoded string containing only
printable characters and spaces. The string will be ``OK`` if reading the pixel
data succeeded or an error message.
Sometimes, using an id is not appropriate, for example, if you do not want to
replace a previously sent image with the same id, or if you are sending a dummy
image and do not want it stored by the terminal emulator. In that case, you can
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
replace an existing image with the same id, nor will it store the image.
Display images on screen
-----------------------------
Every transmitted image can be displayed an arbitrary number of times on the
screen, in different locations, using different parts of the source image, as
needed. You can either simultaneously transmit and display an image using the
action ``a=T``, or first transmit the image with a id, such as ``i=10`` and then display
it with ``a=p,i=10`` which will display the previously transmitted image at the current
cursor position. When specifying an image id, the terminal emulator will reply with an
acknowledgement code, which will be either::
<ESC>_Gi=<id>;OK<ESC>\
when the image referred to by id was found, or::
<ESC>_Gi=<id>;ENOENT:<some detailed error msg><ESC>\
when the image with the specified id was not found. This is similar to the
scheme described above for querying available transmission media, except that
here we are querying if the image with the specified id is available or needs to
be re-transmitted.
Controlling displayed image layout
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The image is rendered at the current cursor position, from the upper left corner of
the current cell. You can also specify extra ``X=3`` and ``Y=4`` pixel offsets to display from
a different origin within the cell. Note that the offsets must be smaller that the size of the cell.
By default, the entire image will be displayed (images wider than the available
width will be truncated on the right edge). You can choose a source rectangle (in pixels)
as the part of the image to display. This is done with the keys: ``x, y, w, h`` which specify
the top-left corner, width and height of the source rectangle.
You can also ask the terminal emulator to display the image in a specified rectangle
(num of columns / num of lines), using the control codes ``c,r``. ``c`` is the number of columns
and `r` the number of rows. The image will be scaled (enlarged/shrunk) as needed to fit
the specified area. Note that if you specify a start cell offset via the ``X,Y`` keys, it is not
added to the number of rows/columns.
Finally, you can specify the image *z-index*, i.e. the vertical stacking order. Images
placed in the same location with different z-index values will be blended if
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
allows rendering of text on top of images.
Deleting images
---------------------
Images can be deleted by using the delete action ``a=d``. If specified without any
other keys, it will delete all images visible on screen. To delete specific images,
use the `d` key as described in the table below. Note that each value of d has
both a lowercase and an uppercase variant. The lowercase variant only deletes the
images without necessarily freeing up the stored image data, so that the images can be
re-displayed without needing to resend the data. The uppercase variants will delete
the image data as well, provided that the image is not referenced elsewhere, such as in the
scrollback buffer. The values of the ``x`` and ``y`` keys are the same as cursor positions (i.e.
``x=1, y=1`` is the top left cell).
================= ============
Value of ``d`` Meaning
================= ============
``a`` or ``A`` Delete all images visible on screen
``i`` or ``I`` Delete all images with the specified id, specified using the ``i`` key.
``c`` or ``C`` Delete all images that intersect with the current cursor position.
``p`` or ``P`` Delete all images that intersect a specific cell, the cell is specified using the ``x`` and ``y`` keys
``q`` or ``Q`` Delete all images that intersect a specific cell having a specific z-index. The cell and z-index is specified using the ``x``, ``y`` and ``z`` keys.
``x`` or ``X`` Delete all images that intersect the specified column, specified using the ``x`` key.
``y`` or ``Y`` Delete all images that intersect the specified row, specified using the ``y`` key.
``z`` or ``Z`` Delete all images that have the specified z-index, specified using the ``z`` key.
================= ============
Some examples::
<ESC>_Ga=d<ESC>\ # delete all visible images
<ESC>_Ga=d,i=10<ESC>\ # delete the image with id=10
<ESC>_Ga=Z,z=-1<ESC>\ # delete the images with z-index -1, also freeing up image data
<ESC>_Ga=P,x=3,y=4<ESC>\ # delete all images that intersect the cell at (3, 4)
Image persistence and storage quotas
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In order to avoid *Denial-of-Service* attacks, terminal emulators should have a
maximum storage quota for image data. It should allow at least a few full
screen images. For example the quota in kitty is 320MB per buffer. When adding
a new image, if the total size exceeds the quota, the terminal emulator should
delete older images to make space for the new one.
Control data reference
---------------------------
The table below shows all the control data keys as well as what values they can
take, and the default value they take when missing. All integers are 32-bit.
======= ==================== ========= =================
Key Value Default Description
======= ==================== ========= =================
``a`` Single character. ``t`` The overall action this graphics command is performing.
``(t, T, q, p, d)``
**Keys for image transmission**
-----------------------------------------------------------
``f`` Positive integer. ``32`` The format in which the image data is sent.
``(24, 32, 100)``.
``t`` Single character. ``d`` The transmission medium used.
``(d, f, t, s)``.
``s`` Positive integer. ``0`` The width of the image being sent.
``v`` Positive integer. ``0`` The height of the image being sent.
``S`` Positive integer. ``0`` The size of data to read from a file.
``O`` Positive integer. ``0`` The offset from which to read data from a file.
``i`` Positive integer.
``(0 - 4294967295)`` ``0`` The image id
``o`` Single character. ``null`` The type of data compression.
``only z``
``m`` zero or one ``0`` Whether there is more chunked data available.
**Keys for image display**
-----------------------------------------------------------
``x`` Positive integer ``0`` The left edge (in pixels) of the image area to display
``y`` Positive integer ``0`` The top edge (in pixels) of the image area to display
``w`` Positive integer ``0`` The width (in pixels) of the image area to display. By default, the entire width is used.
``h`` Positive integer ``0`` The height (in pixels) of the image area to display. By default, the entire height is used
``X`` Positive integer ``0`` The x-offset within the first cell at which to start displaying the image
``Y`` Positive integer ``0`` The y-offset within the first cell at which to start displaying the image
``c`` Positive integer ``0`` The number of columns to display the image over
``r`` Positive integer ``0`` The number of rows to display the image over
``z`` Integer ``0`` The *z-index* vertical stacking order of the image
**Keys for deleting images**
-----------------------------------------------------------
``d`` Single character. ``a`` What to delete.
``(a, A, c, C, p,
P, q, Q, x, X, y,
Y, z, Z)``.
======= ==================== ========= =================
Interaction with other terminal actions
--------------------------------------------
When resetting the terminal, all images that are visible on the screen must be
cleared. When switching from the main screen to the alternate screen buffer
(1049 private mode) all images in the alternate screen must be cleared, just as
all text is cleared. The clear screen escape code (usually ``<ESC>[2J``) should
also clear all images. This is so that the clear command works.
The other commands to erase text must have no effect on graphics.
The dedicated delete graphics commands must be used for those.
When scrolling the screen (such as when using index cursor movement commands,
or scrolling through the history buffer), images must be scrolled along with
text. When page margins are defined and the index commands are used, only
images that are entirely within the page area (between the margins) must be
scrolled. When scrolling them would cause them to extend outside the page area,
they must be clipped.

352
docs/index.rst Normal file
View File

@@ -0,0 +1,352 @@
:tocdepth: 2
==========================================================
kitty - the fast, featureful, GPU based terminal emulator
==========================================================
.. container:: major-features
* Offloads rendering to the GPU for :doc:`lower system load <performance>` and
buttery smooth scrolling. Uses threaded rendering to minimize input latency.
* Supports all modern terminal features: :doc:`graphics (images)
<graphics-protocol>`, unicode, true-color, OpenType ligatures, mouse
protocol, focus tracking, `bracketed paste
<https://cirw.in/blog/bracketed-paste>`_ and several :doc:`new terminal
protocol extensions <protocol-extensions>`.
* Supports tiling multiple terminal windows side by side in different
:ref:`layouts <layouts>` without needing to use an extra program like tmux
* Can be :doc:`controlled from scripts or the shell prompt <remote-control>`,
even over SSH.
* Has a framework for :ref:`kittens`, small terminal programs that can be used to
extend |kitty|'s functionality. For example, they are used for
:doc:`Unicode input <kittens/unicode-input>`, :doc:`Hints <kittens/hints>` and
:doc:`Side-by-side diff <kittens/diff>`.
* Supports :ref:`startup sessions <sessions>` which allow you to specify
the window/tab layout, working directories and programs to run on startup.
* Cross-platform: |kitty| works on Linux and macOS, but because it uses only
OpenGL for rendering, it should be trivial to port to other platforms.
* Allows you to open :ref:`the scrollback buffer <scrollback>` in a
separate window using arbitrary programs of your choice. This is useful for
browsing the history comfortably in a pager or editor.
.. figure:: screenshots/screenshot.png
:alt: Screenshot, showing three programs in the 'Tall' layout
:align: center
:scale: 100%
Screenshot, showing vim, tig and git running in |kitty| with the 'Tall' layout
.. _quickstart:
Quickstart
--------------
Pre-built binaries of |kitty| are available for both macOS and Linux.
See the :doc:`binary install instructions </binary>`.
If you are on Linux, you can also use your distribution's |kitty| package.
|kitty| packages are available for:
`Debian <https://packages.debian.org/buster/kitty>`_,
`openSUSE <https://build.opensuse.org/package/show/X11:terminals/kitty>`_,
`Arch Linux <https://www.archlinux.org/packages/community/x86_64/kitty/>`_,
`NixOS <https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/misc/kitty/default.nix>`_,
`Gentoo <https://packages.gentoo.org/packages/x11-terms/kitty>`_,
`Fedora <https://copr.fedorainfracloud.org/coprs/oleastre/kitty-terminal/>`_.
See :doc:`Configuring kitty <conf>` for help on configuring |kitty| and
:doc:`Invocation <invocation>` for the command line arguments |kitty| supports.
.. contents::
Design philosophy
-------------------
|kitty| is designed for power keyboard users. To that end all its controls
work with the keyboard (although it fully supports mouse interactions as
well). Its configuration is a simple, human editable, single file for
easy reproducibility (I like to store configuration in source control).
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
easy hackability of the UI). It does not depend on any large and complex
UI toolkit, using only OpenGL for rendering everything.
Finally, |kitty| is designed from the ground up to support all modern
terminal features, such as unicode, true color, bold/italic fonts, text
formatting, etc. It even extends existing text formatting escape codes,
to add support for features not available elsewhere, such as colored and
styled (curly) underlines. One of the design goals of |kitty| is to be
easily extensible so that new features can be added in the future with
relatively less effort.
Tabs and Windows
-------------------
|kitty| is capable of running multiple programs organized into tabs and
windows. The top level of organization is the *Tab*. Each tab consists
of one or more *windows*. The windows can be arranged in multiple
different layouts, like windows are organized in a tiling window
manager. The keyboard controls (which are all customizable) for tabs and
windows are:
Scrolling
~~~~~~~~~~~~~~
======================== =======================
Action Shortcut
======================== =======================
Scroll line up :sc:`scroll_line_up`
Scroll line down :sc:`scroll_line_down`
Scroll page up :sc:`scroll_page_up`
Scroll page down :sc:`scroll_page_down`
Scroll to top :sc:`scroll_home`
Scroll to bottom :sc:`scroll_end`
======================== =======================
Tabs
~~~~~~~~~~~
======================== =======================
Action Shortcut
======================== =======================
New tab :sc:`new_tab`
Close tab :sc:`close_tab`
Next tab :sc:`next_tab`
Previous tab :sc:`previous_tab`
Next layout :sc:`next_layout`
Move tab forward :sc:`move_tab_forward`
Move tab backward :sc:`move_tab_backward`
Set tab title :sc:`set_tab_title`
======================== =======================
Windows
~~~~~~~~~~~~~~~~~~
======================== =======================
Action Shortcut
======================== =======================
New window :sc:`new_window`
New OS window :sc:`new_os_window` (also :kbd:`⌘+n` on macOS)
Close window :sc:`close_window`
Next window :sc:`next_window`
Previous window :sc:`previous_window`
Move window forward :sc:`move_window_forward`
Move window backward :sc:`move_window_backward`
Move window to top :sc:`move_window_to_top`
Focus specific window :sc:`first_window`, :sc:`second_window` ... :sc:`tenth_window`
(clockwise from the top-left)
======================== =======================
Other keyboard shortcuts
----------------------------------
================================== =======================
Action Shortcut
================================== =======================
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 selection :sc:`paste_from_selection`
Increase font size :sc:`increase_font_size`
Decrease font size :sc:`decrease_font_size`
Restore font size :sc:`reset_font_size`
Toggle fullscreen :sc:`toggle_fullscreen`
Input unicode character :sc:`input_unicode_character`
Click URL using the keyboard :sc:`open_url`
Pass current selection to program :sc:`pass_selection_to_program`
Edit |kitty| config file :sc:`edit_config_file`
Open a |kitty| shell :sc:`kitty_shell`
Increase background opacity :sc:`increase_background_opacity`
Decrease background opacity :sc:`decrease_background_opacity`
Full background opacity :sc:`full_background_opacity`
Reset background opacity :sc:`reset_background_opacity`
================================== =======================
.. _layouts:
Layouts
----------
Currently, there are five layouts available,
* **Stack** -- Only a single maximized window is shown at a time
* **Tall** -- One window is shown full height on the left, the rest of the windows are shown one below the other on the right
* **Fat** -- One window is 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
* **Horizontal** -- All windows are shown side-by-side
* **Vertical** -- All windows are shown one below the other
You can switch between layouts using 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 :ref:`conf-kitty-shortcuts.layout` for examples.
You can resize windows inside layouts. Press :sc:`start_resizing_window` to
enter resizing mode and follow the on-screen instructions. In a given window
layout only some operations may be possible for a particular window. For
example, in the Tall layout you can make the first 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 row/column will be resized.
Some layouts take options to control their behavior. For example, the ``fat``
and ``tall`` layouts accept the ``bias`` option to control how the available
space is split up. To specify the option, in :opt:`kitty.conf <enabled_layouts>` use::
enabled_layouts tall:bias=70
This will make the tall window occupy ``70%`` of available width. ``bias`` can be
any number between 10 and 90.
Writing a new layout only requires about a hundred lines of code, so if there
is some layout you want, take a look at `layout.py
<https://github.com/kovidgoyal/kitty/blob/master/kitty/layout.py>`_ and submit
a pull request!
.. _kittens:
Kittens
------------------
|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
to add features to |kitty| itself and to create useful standalone programs.
Some prominent kittens:
:doc:`icat <kittens/icat>`
Display images in the terminal
:doc:`diff <kittens/diff>`
A fast, side-by-side diff for the terminal with syntax highlighting and
images
:doc:`Unicode Input <kittens/unicode-input>`
Easily input arbitrary unicode characters in |kitty| by name or hex code.
:doc:`Hints <kittens/hints>`
Select and open/paste/insert arbitrary text snippets such as URLs,
filenames, words, lines, etc from the terminal screen.
:doc:`Panel <kittens/panel>`
Draw a GPU accelerated dock panel on your desktop showing the output
from an arbitrary terminal program.
:doc:`Clipboard <kittens/clipboard>`
Copy/paste to the clipboard from shell scripts, even over SSH.
Configuring kitty
-------------------
|kitty| is highly configurable, everything from keyboard shortcuts to
painting frames-per-second. For details and a sample :file:`kitty.conf`,
see the :doc:`configuration docs <conf>`.
.. _sessions:
Startup Sessions
------------------
You can control the tabs, window layout, working directory, startup programs,
etc. by creating a "session" file and using the :option:`kitty --session`
command line flag or the :opt:`startup_session` option in :file:`kitty.conf`.
For example:
.. code-block:: session
# Set the window layout for the current tab
layout tall
# Set the working directory for windows in the current tab
cd ~
# Create a window and run the specified command in it
launch zsh
# Create a window with some environment variables set and run
# vim in it
launch env FOO=BAR vim
# Set the title for the next window
title Chat with x
launch irssi --profile x
# Create a new tab (the part after new_tab is the optional tab
# name which will be displayed in the tab bar, if omitted, the
# title of the active window will be used instead)
new_tab my tab
cd ~/somewhere
# Set the layouts allowed in this tab
enabled_layouts tall, stack
# Set the current layout
layout stack
launch zsh
# Make the current window the active (focused) window
focus
launch emacs
Mouse features
-------------------
* You can also hold down :kbd:`ctrl+shift` and click on a URL to open it in a browser.
* You can double click to select a word and triple click to select a line.
* You can right click to extend a previous selection
Font control
-----------------
|kitty| has extremely flexible and powerful font selection features. You can
specify individual families for the regular, bold, italic and bold+italic
fonts. You can even specify specific font families for specific ranges of
unicode characters. This allows precise control over text rendering. It can
come in handy for applications like powerline, without the need to use patched
fonts. See the various font related configuration directives in
:ref:`conf-kitty-fonts`.
.. _scrollback:
The scrollback buffer
-----------------------
|kitty| supports scrolling back to view history, just like most terminals. You
can use either keyboard shortcuts or the mouse scroll wheel to do so. However,
|kitty| has an extra, neat feature. Sometimes you need to explore the
scrollback buffer in more detail, maybe search for some text or refer to it
side-by-side while typing in a follow-up command. |kitty| allows you to do this
by pressing the :sc:`show_scrollback` key-combination, which will open the
scrollback buffer in your favorite pager program (which is ``less`` by default).
Colors and text formatting are preserved. You can explore the scrollback buffer
comfortably within the pager.
Frequently Asked Questions
---------------------------------
The list of Frequently Asked Questions (*FAQ*) is :doc:`available here <faq>`.
Changelog
------------------
See :doc:`changelog`.
.. toctree::
:hidden:
:glob:
*
kittens/*

241
docs/installer.py Normal file
View File

@@ -0,0 +1,241 @@
#!/usr/bin/env python
# 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()
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_latest_release_data():
print('Checking for latest release on GitHub...')
req = urllib.Request('https://api.github.com/repos/kovidgoyal/kitty/releases/latest', headers={'Accept': 'application/vnd.github.v3+json'})
res = urllib.urlopen(req)
data = json.load(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:
if name.endswith('-x86_64.txz'):
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')
machine = os.uname()[4]
if machine and machine.lower().startswith('arm'):
raise SystemExit(
'You are running on an ARM system. The kitty binaries are only'
' available for x86 systems. You will have to build from'
' source.')
if not installer:
url, size = get_latest_release_data()
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_intaller_wrapper():
# To run: python3 -c "import runpy; runpy.run_path('installer.py', run_name='update_wrapper')" installer.sh
src = open(__file__, 'rb').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_intaller_wrapper()
elif __name__ == 'script_launch':
script_launch()

30
docs/installer.sh Normal file
View File

@@ -0,0 +1,30 @@
#!/bin/sh
#
# installer.sh
# Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>
#
# Distributed under terms of the GPLv3 license.
#
python=$(command -v python3)
if [ -z "$python" ]; then
python=$(command -v python2)
fi
if [ -z "$python" ]; then
python=$(command -v python2.7)
fi
if [ -z "$python" ]; then
python=$(command -v python)
fi
if [ -z "$python" ]; then
python=python
fi
echo Using python executable: $python
$python -c "import sys; script_launch=lambda:sys.exit('Download of installer failed!'); exec(sys.stdin.read()); script_launch()" "$@" <<'INSTALLER_HEREDOC'
# {{{
# HEREDOC_START
# HEREDOC_END
# }}}
INSTALLER_HEREDOC

6
docs/invocation.rst Normal file
View File

@@ -0,0 +1,6 @@
The kitty command line interface
====================================
.. program:: kitty
.. include:: generated/cli-kitty.rst

129
docs/key-encoding.rst Normal file
View File

@@ -0,0 +1,129 @@
Key encoding for extended keyboard protocol
===============================================
See :ref:`extended-key-protocol` for more information and `this table in JSON
format <https://github.com/kovidgoyal/kitty/blob/master/key_encoding.json>`_.
===================== ======================
Name Encoded representation (base64)
0 ``G``
1 ``H``
2 ``I``
3 ``J``
4 ``K``
5 ``L``
6 ``M``
7 ``N``
8 ``O``
9 ``P``
A ``S``
APOSTROPHE ``B``
B ``T``
BACKSLASH ``t``
BACKSPACE ``1``
C ``U``
CAPS LOCK ``:``
COMMA ``C``
D ``V``
DELETE ``3``
DOWN ``6``
E ``W``
END ``-``
ENTER ``z``
EQUAL ``R``
ESCAPE ``y``
F ``X``
F1 ``/``
F10 ``]``
F11 ``{``
F12 ``}``
F13 ``@``
F14 ``%``
F15 ``$``
F16 ``#``
F17 ``BA``
F18 ``BB``
F19 ``BC``
F2 ``*``
F20 ``BD``
F21 ``BE``
F22 ``BF``
F23 ``BG``
F24 ``BH``
F25 ``BI``
F3 ``?``
F4 ``&``
F5 ``<``
F6 ``>``
F7 ``(``
F8 ``)``
F9 ``[``
G ``Y``
GRAVE ACCENT ``v``
H ``Z``
HOME ``.``
I ``a``
INSERT ``2``
J ``b``
K ``c``
KP 0 ``BJ``
KP 1 ``BK``
KP 2 ``BL``
KP 3 ``BM``
KP 4 ``BN``
KP 5 ``BO``
KP 6 ``BP``
KP 7 ``BQ``
KP 8 ``BR``
KP 9 ``BS``
KP ADD ``BX``
KP DECIMAL ``BT``
KP DIVIDE ``BU``
KP ENTER ``BY``
KP EQUAL ``BZ``
KP MULTIPLY ``BV``
KP SUBTRACT ``BW``
L ``d``
LEFT ``5``
LEFT ALT ``Bc``
LEFT BRACKET ``s``
LEFT CONTROL ``Bb``
LEFT SHIFT ``Ba``
LEFT SUPER ``Bd``
M ``e``
MINUS ``D``
N ``f``
NUM LOCK ``=``
O ``g``
P ``h``
PAGE DOWN ``9``
PAGE UP ``8``
PAUSE ``!``
PERIOD ``E``
PRINT SCREEN ``^``
Q ``i``
R ``j``
RIGHT ``4``
RIGHT ALT ``Bg``
RIGHT BRACKET ``u``
RIGHT CONTROL ``Bf``
RIGHT SHIFT ``Be``
RIGHT SUPER ``Bh``
S ``k``
SCROLL LOCK ``+``
SEMICOLON ``Q``
SLASH ``F``
SPACE ``A``
T ``l``
TAB ``0``
U ``m``
UP ``7``
V ``n``
W ``o``
WORLD 1 ``w``
WORLD 2 ``x``
X ``p``
Y ``q``
Z ``r``
===================== ======================

View File

@@ -0,0 +1,27 @@
clipboard - copy/paste to the system clipboard
==================================================
.. highlight:: sh
The ``clipboard`` kitten can be used to read or write to the system clipboard
from the shell. It even works over SSH. Using it is as simple as::
echo hooray | kitty +kitten clipboard
All text received on :file:`stdin` is copied to the clipboard.
To get text from the clipboard you have to enable reading of the clipboard
in :opt:`clipboard_control` in :file:`kitty.conf`. Once you do that, you can
use::
kitty +kitten clipboard --get-clipboard
.. program:: kitty +kitten clipboard
Command Line Interface
--------------------------
.. include:: /generated/cli-kitten-clipboard.rst

157
docs/kittens/diff.rst Normal file
View File

@@ -0,0 +1,157 @@
kitty-diff - A fast side-by-side diff tool with syntax highlighting and images
================================================================================
.. highlight:: sh
Major Features
-----------------
.. container:: major-features
* Displays diffs side-by-side in the kitty terminal
* Does syntax highlighting of the displayed diffs, asynchronously, for maximum
speed
* Displays images as well as text diffs, even over SSH
* Does recursive directory diffing
.. figure:: ../screenshots/diff.png
:alt: Screenshot, showing a sample diff
:align: center
:scale: 100%
Screenshot, showing a sample diff
.. contents::
Installation
---------------
Simply :ref:`install kitty <quickstart>`. You also need
to have either the `git <https://git-scm.com/>`_ program or the ``diff`` program
installed. Additionally, for syntax highlighting to work,
`pygments <http://pygments.org/>`_ must be installed (note that pygments is
included in the macOS kitty app).
Usage
--------
In the kitty terminal, run::
kitty +kitten diff file1 file2
to see the diff between file1 and file2.
Create an alias in your shell's startup file to shorten the command, for example:
.. code-block:: sh
alias d="kitty +kitten diff"
Now all you need to do to diff two files is::
d file1 file2
You can also pass directories instead of files to see the recursive diff of the
directory contents.
Keyboard controls
----------------------
========================= ===========================
Action Shortcut
========================= ===========================
Quit :kbd:`q, Ctrl+c, Esc`
Scroll line up :kbd:`k, up`
Scroll line down :kbd:`j, down`
Scroll page up :kbd:`PgUp`
Scroll page down :kbd:`PgDn`
Scroll to top :kbd:`Home`
Scroll to bottom :kbd:`End`
Scroll to next page :kbd:`Space, PgDn`
Scroll to previous page :kbd:`PgUp`
Scroll to next change :kbd:`n`
Scroll to previous change :kbd:`p`
Increase lines of context :kbd:`+`
Decrease lines of context :kbd:`-`
All lines of context :kbd:`a`
Restore default context :kbd:`=`
Search forwards :kbd:`/`
Search backwards :kbd:`?`
Clear search :kbd:`Esc`
Scroll to next match :kbd:`>, .`
Scroll to previous match :kbd:`<, ,`
========================= ===========================
Integrating with git
-----------------------
Add the following to `~/.gitconfig`:
.. code-block:: ini
[diff]
tool = kitty
guitool = kitty.gui
[difftool]
prompt = false
trustExitCode = true
[difftool "kitty"]
cmd = kitty +kitten diff $LOCAL $REMOTE
[difftool "kitty.gui"]
cmd = kitty kitty +kitten diff $LOCAL $REMOTE
Now to use kitty-diff to view git diffs, you can simply do::
git difftool --no-symlinks --dir-diff
Once again, creating an alias for this command is useful.
Why does this work only in kitty?
----------------------------------------
The diff kitten makes use of various features that are :doc:`kitty only
</protocol-extensions>`, such as the :doc:`kitty graphics protocol
</graphics-protocol>`, the :ref:`extended keyboard protocol
<extended-key-protocol>`, etc. It also leverages terminal program
infrastructure I created for all of kitty's other kittens to reduce the amount
of code needed (the entire implementation is under 2000 lines of code).
And fundamentally, it's kitty only because I wrote it for myself, and I am
highly unlikely to use any other terminals :)
Configuration
------------------------
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
your :ref:`kitty config folder <confloc>`. See below for the supported
configuration directives.
.. include:: /generated/conf-kitten-diff.rst
Command Line Interface
-------------------------
.. include:: /generated/cli-kitten-diff.rst
Sample diff.conf
-----------------
You can download a sample :file:`diff.conf` file with all default settings and
comments describing each setting by clicking: :download:`sample diff.conf
</generated/conf/diff.conf>`.

27
docs/kittens/hints.rst Normal file
View File

@@ -0,0 +1,27 @@
Hints
==========
|kitty| has a *hints mode* to select and act on arbitrary text snippets 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
browser.
.. figure:: ../screenshots/hints_mode.png
:alt: URL hints mode
:align: center
:scale: 100%
URL hints mode
Similarly, you can press :sc:`insert_selected_path` to
select anything that looks like a path or filename and then insert it into the
terminal, very useful for picking files from the output of a ``git`` or ``ls`` command and
adding them to the command line for the next command.
The hints kitten is very powerful to see more detailed help on its various
options and modes of operation, see below.
Command Line Interface
-------------------------
.. include:: ../generated/cli-kitten-hints.rst

29
docs/kittens/icat.rst Normal file
View File

@@ -0,0 +1,29 @@
icat - Display images in the terminal
========================================
The ``icat`` kitten can be used to display arbitrary images in the |kitty|
terminal. Using it is as simple as::
kitty +kitten icat image.jpeg
It supports all image types supported by `ImageMagick
<https://www.imagemagick.org>`_. It even works over SSH. For details, see
the :doc:`kitty graphics protocol </graphics-protocol>`.
.. note::
`ImageMagick <https://www.imagemagick.org>`_ must be installed for ``icat`` to
work.
.. program:: kitty +kitten icat
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`,
:option:`--detect-support` and :option:`--print-window-size`.
Command Line Interface
--------------------------
.. include:: /generated/cli-kitten-icat.rst

43
docs/kittens/panel.rst Normal file
View File

@@ -0,0 +1,43 @@
Draw a GPU accelerated dock panel on your desktop
====================================================================================================
.. highlight:: sh
You can use this kitten to draw a GPU accelerated panel on the edge
of your screen, that shows the output from an arbitrary terminal program.
It is useful for showing status information or notifications on your desktop
using terminal programs instead of GUI toolkits.
.. figure:: ../screenshots/panel.png
:alt: Screenshot, showing a sample panel
:align: center
:scale: 100%
Screenshot, showing a sample panel
The screenshot above shows a sample panel that displays the current desktop and
window title as well as miscellaneous system information such as network
activity, CPU load, date/time, etc.
.. note::
This kitten currently only works on X11 desktops
Using this kitten is simple, for example::
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
seconds. Here the terminal program we are running is ``sh`` with a script to
print out ``Hello, world!``. You can make the terminal program as complex as
you like, as demonstrated in the screenshot above.
Command Line Interface
-------------------------
.. include:: ../generated/cli-kitten-panel.rst

View File

@@ -0,0 +1,22 @@
Unicode input
================
You can input unicode characters by name, hex code, recently used and even an editable favorites list.
Press :sc:`input_unicode_character` to start the unicode input widget, shown below.
.. figure:: ../screenshots/unicode.png
:alt: A screenshot of the unicode input widget
:align: center
:scale: 100%
A screenshot of the unicode input widget
In :guilabel:`Code` mode, you enter a unicode character by typing in the hex code for the
character and pressing enter, for example, type in ``2716`` and press enter to get
✖. You can also choose a character from the list of recently used characters by
typing a leading period and then the two character index and pressing Enter.
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 also type
a leading period and the index for the match if you dont like to use arrow
keys.

59
docs/performance.rst Normal file
View File

@@ -0,0 +1,59 @@
|kitty| Performance
===================
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
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
with child programs takes place in a separate thread from rendering, to improve
smoothness.
There are two parameters you can tune to adjust the performance. ``repaint_delay``
and ``input_delay``. These control the artificial delays introduced into the
render loop to reduce CPU usage. See :ref:`conf-kitty-performance` for details.
See also the ``sync_to_monitor`` option to further decrease latency at the cost
of some `tearing <https://en.wikipedia.org/wiki/Screen_tearing>`_ while scrolling.
You can generate detailed per-function performance data using
`gperftools <https://github.com/gperftools/gperftools>`_. Build |kitty| with
`make profile` which will create an executable called `kitty-profile`. Run
that and perform the task you want to analyse, for example, scrolling a large
file with `less`. After you quit, function call statistics will be printed to
`stdout` and you can use tools like *kcachegrind* for more detailed analysis.
Here are some CPU usage numbers for the task of scrolling a file continuously
in 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
size for all terminals on a ``Intel(R) Core(TM) i7-4820K CPU @ 3.70GHz`` CPU
with a ``Advanced Micro Devices, Inc. [AMD/ATI] Cape Verde XT [Radeon HD
7770/8760 / R7 250X]`` GPU.
============== =========================
Terminal CPU usage (X + terminal)
============== =========================
|kitty| 6 - 8%
xterm 5 - 7% (but scrolling was extremely janky)
termite 10 - 13%
urxvt 12 - 14%
gnome-terminal 15 - 17%
konsole 29 - 31%
============== =========================
As you can see, |kitty| uses much less CPU than all terminals, except xterm, but
its scrolling "smoothness" is much better than that of xterm (at least to my,
admittedly biased, eyes).
.. _perf-cat:
.. note::
Some people have asked why kitty does not perform better than terminal XXX 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
deliberately throttles input parsing and output rendering to minimize
resource usage while still being able to sink output faster than any real
world program can produce it. Reducing CPU usage, and hence battery drain
while achieving instant response times and smooth scrolling to a human eye
is a far more important goal.

View File

@@ -0,0 +1,186 @@
Extensions to the xterm protocol
===================================
|kitty| has a few extensions to the xterm protocol, to enable advanced features.
These are typically in the form of new or re-purposed escape codes. While these
extensions are currently |kitty| specific, it would be nice to get some of them
adopted more broadly, to push the state of terminal emulators forward.
The goal of these extensions is to be as small an unobtrusive as possible,
while filling in some gaps in the existing xterm protocol. In particular, one
of the goals of this specification is explicitly not to "re-imagine" the tty.
The 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
possible amount of extra functionality into the terminal program itself. This
is to make it as easy to implement these protocol extensions as possible,
thereby hopefully encouraging their widespread adoption.
If you wish to discuss these extensions, propose additions/changes to them
please do so by opening issues in the github bug tracker.
.. contents::
Colored and styled underlines
-------------------------------
|kitty| supports colored and styled (wavy) underlines. This is of particular
use in terminal editors such as vim and emacs to display red, wavy underlines
under mis-spelled words and/or syntax errors. This is done by re-purposing some
SGR escape codes that are not used in modern terminals (`CSI codes
<https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences>`_)
To set the underline style::
<ESC>[4:0m # this is no underline
<ESC>[4:1m # this is a straight underline
<ESC>[4:2m # this is a double underline
<ESC>[4:3m # this is a curly underline
<ESC>[4:4m # this is a dotted underline (not implemented in kitty)
<ESC>[4:5m # this is a dashed underline (not implemented in kitty)
<ESC>[4m # this is a straight underline (for backwards compat)
<ESC>[24m # this is 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)::
<ESC>[58...m
This works exactly like the codes ``38, 48`` that are used to set foreground and
background color respectively.
To reset the underline color (also previously reserved and unused)::
<ESC>[59m
To detect support for this feature in a terminal emulator, query the terminfo database
for the ``Su`` boolean capability.
Graphics rendering
---------------------
See :doc:`/graphics-protocol` for a description
of this protocol to enable drawing of arbitrary raster images in the terminal.
.. _extended-key-protocol:
Keyboard handling
-------------------
There are various problems with the current state of keyboard handling. They
include:
* No way to use modifiers other than ``Ctrl`` and ``Alt``
* No way to reliably use multiple modifier keys, other than, ``Shift+Alt``.
* No way to handle different types of keyboard events, such as press, release or repeat
* No reliable way to distinguish single ``Esc`` keypresses from the start of a
escape sequence. Currently, client programs use fragile timing related hacks
for this, leading to bugs, for example:
`neovim #2035 <https://github.com/neovim/neovim/issues/2035>`_.
There are already two distinct keyboard handling modes, *normal mode* and
*application mode*. These modes generate different escape sequences for the
various special keys (arrow keys, function keys, home/end etc.) Most terminals
start out in normal mode, however, most shell programs like ``bash`` switch them to
application mode. We propose adding a third mode, named *full mode* that addresses
the shortcomings listed above.
Switching to the new *full mode* is accomplished using the standard private
mode DECSET escape sequence::
<ESC>[?2017h
and to leave *full mode*, use DECRST::
<ESC>[?2017l
The number ``2017`` above is not used for any existing modes, as far as I know.
Client programs can query if the terminal emulator is in *full mode* by using
the standard `DECRQM <https://vt100.net/docs/vt510-rm/DECRQM.html>`_ escape sequence.
The new mode works as follows:
* All printable key presses without modifier keys are sent just as in the
*normal mode*. This means all printable ASCII characters and in addition,
``Enter``, ``Space`` and ``Backspace``. Also any unicode characters generated by
platform specific extended input modes, such as using the ``AltGr`` key. This
is done so that client programs that are not aware of this mode can still
handle basic text entry, so if a *full mode* using program crashes and does
not reset, the user can still issue a ``reset`` command in the shell to restore
normal key handling. Note that this includes pressing the ``Shift`` modifier
and printable keys. Note that this means there are no repeat and release
events for these keys and also for the left and right shift keys.
* For non printable keys and key combinations including one or more modifiers,
an escape sequence encoding the key event is sent. For details on the
escape sequence, see below.
The escape sequence encodes the following properties:
* Type of event: ``press,repeat,release``
* Modifiers pressed at the time of the event
* The actual key being pressed
Schematically::
<ESC>_K<type><modifiers><key><ESC>\
Where ``<type>`` is one of ``p`` -- press, ``r`` -- release and ``t`` -- repeat.
Modifiers is a bitmask represented as a single base64 digit. Shift -- ``0x1``,
Alt -- ``0x2``, Control -- ``0x4`` and Super -- ``0x8``. ``<key>`` is a number
(encoded in base85) corresponding to the key pressed. The key name to number
mapping is defined in :doc:`this table <key-encoding>`.
For example::
<ESC>_KpGp<ESC>\ is <Ctrl>+<Alt>+x (press)
<ESC>_KrP8<ESC>\ is <Ctrl>+<Alt>+<Shift>+<Super>+PageUp (release)
This encoding means each key event is represented by 8 or 9 printable ascii
only bytes, for maximum robustness.
.. _ext_styles:
Setting text styles/colors in arbitrary regions of the screen
------------------------------------------------------------------
There already exists an escape code to set *some* text attributes in arbitrary
regions of the screen, `DECCARA
<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.
So, for example, this can be used to set the background color in an arbitrary
region of the screen.
The motivation for this extension is the various problems with the existing
solution for erasing to background color, namely the *background color erase
(bce)* capability. See
`this discussion <https://github.com/kovidgoyal/kitty/issues/160#issuecomment-346470545>`_
and `this FAQ <http://invisible-island.net/ncurses/ncurses.faq.html#bce_mismatches>`_
for a summary of problems with *bce*.
For example, to set the background color to blue in a
rectangular region of the screen from (3, 4) to (10, 11), you use::
<ESC>[2*x<ESC>[4;3;11;10;44$r<ESC>[*x
Saving and restoring the default foreground/background/selection/cursor colors
---------------------------------------------------------------------------------
It is often useful for a full screen application with its own color themes
to set the default foreground, background, selection and cursor colors. This
allows for various performance optimizations when drawing the screen. The
problem is that if the user previously used the escape codes to change these
colors herself, then running the full screen application will lose her
changes even after it exits. To avoid this, kitty introduces a new pair of
*OSC* escape codes to push and pop the current color values from a stack::
<ESC>]30001<ESC>\ # push onto stack
<ESC>]30101<ESC>\ # pop from stack
These escape codes save/restore the so called *dynamic colors*, default
background, default foreground, selection background, selection foreground and
cursor color.

122
docs/remote-control.rst Normal file
View File

@@ -0,0 +1,122 @@
:tocdepth: 2
Controlling kitty from scripts or the shell
==============================================
.. highlight:: sh
Tutorial
----------
|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.
Let's walk through a few examples of controlling |kitty|.
Start by running |kitty| as::
kitty -o allow_remote_control=yes --window-layout tall
In order for control to work, :opt:`allow_remote_control` 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::
kitty @ new-window --title Output --keep-focus cat
This will open a new window, running the ``cat`` program that will appear next
to the current window.
Let's send some text to this new window::
kitty @ send-text --match cmdline:cat Hello, World
This will make ``Hello, World`` show up in the window running the ``cat`` program.
The :option:`kitty @ send-text --match` option is very powerful, it allows selecting windows by their
titles, the command line of the program running in the window, the working
directory of the program running in the window, etc. See ``kitty @ send-text
--help`` for details.
More usefully, you can pipe the output of a command running in one window to
another window, for example::
ls | kitty @ send-text --match title:Output --stdin
This will show the output of ls in the output window instead of the current
window. You can use this technique to, for example, show the output of running
``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::
kitty @ send-text --match title:Output --stdin
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.
Now, let's open a new tab::
kitty @ new-window --new-tab --tab-title "My Tab" --keep-focus bash
This will open a new tab running the bash shell with the title "My Tab".
We can change the title of the tab with::
kitty @ set-tab-title --match title:My New Title
Let's change the title of the current tab::
kitty @ set-tab-title Master Tab
Now lets switch to the newly opened tab::
kitty @ focus-tab --match title:New
Similarly, to focus the previously opened output window (which will also switch
back to the old tab, automatically)::
kitty @ focus-window --match title:Output
You can get a listing of available tabs and windows, by running::
kitty @ ls
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.
Each tab has its own id, a title and a list of windows. Each window has an id,
title, current working directory, process id (PID) and command-line of the
process running in the window. You can 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
``kitty @`` messaging system. This tutorial touches only the
surface of what is possible. See ``kitty @ --help`` for more details.
Note that in the example's above, ``kitty @`` messaging works only when run inside a |kitty| window,
not anywhere. But, within a |kitty| window it even works over SSH. If you want to control
|kitty| from programs/scripts not running inside a |kitty| window, you have to implement a couple of
extra steps. First start |kitty| as::
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
specified path. See ``kitty --help`` for details. 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
Note that is 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
--------------------------
You can explore the |kitty| command language more easily using the builtin |kitty|
shell. Run ``kitty @`` with no arguments and you will be dropped into 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
keyboard shortcut (:sc:`kitty_shell` by default). This has the added
advantage that you dont need to use ``allow_remote_control`` to make it work.
.. include:: generated/cli-kitty-at.rst

View File

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

View File

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

BIN
docs/screenshots/panel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

69
docs/support.html Normal file
View File

@@ -0,0 +1,69 @@
<style>
.support-button {
border-radius: 6px;
box-shadow: rgb(255, 246, 175) 0px 1px 0px 0px;
display: inline-block;
cursor: pointer;
color: rgb(51, 51, 51);
font-size: inherit;
font-family: Arial;
font-weight: bold;
padding: 8px 24px;
text-decoration: none;
}
.support-button:hover {
transform: scale(1.5);
}
.support-button {
outline: 0;
}
.support-button:visited {
color: rgb(51, 51, 51);
outline: 0;
}
#support-buttons > div {
margin-top: 3ex;
text-align: center;
}
#paypal input[type=submit] {
background: linear-gradient(rgb(255, 236, 100) 5%, rgb(255, 171, 35) 100%) rgb(255, 236, 100);
border: 1px solid rgb(255, 171, 35);
}
#liberapay a {
background: linear-gradient(rgb(92, 184, 92) 5%, rgb(62, 142, 62) 100%) rgb(92, 184, 92);
border: 1px solid rgb(62, 142, 62);
color: rgb(51, 51, 51);
}
#patreon a {
background: linear-gradient(rgb(11, 100, 163) 5%, rgb(5, 45, 73) 100%) rgb(11, 100, 163);
border: 1px solid rgb(5, 45, 73);
color: #eee;
}
</style>
<div id="support-buttons">
<div id="patreon">
<a class="support-button" href="https://www.patreon.com/bePatron?u=917933">Patronage via Patreon</a>
</div>
<div>
<form id="paypal" action="https://www.paypal.com/cgi-bin/webscr" method="post" title="Contribute to support calibre development">
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="hosted_button_id" value="FEELASHWEL5HS" />
<input class="support-button" type="submit" name="submit" value="One time support via PayPal" />
<img alt="" border="0" src="https://www.paypalobjects.com/en_GB/i/scr/pixel.gif" width="1" height="1" />
</form>
</div>
<div id="liberapay">
<a class="support-button" href="https://liberapay.com/kovidgoyal/donate">Patronage via Liberapay</a>
</div>
</div>

23
docs/support.rst Normal file
View File

@@ -0,0 +1,23 @@
Support kitty development
===========================
My goal with |kitty| is to move the stagnant terminal ecosystem forward. To that
end kitty has many foundational features, such as: :doc:`image support
<graphics-protocol>`, :doc:`superlative performance <performance>`,
:doc:`various enhancements to the terminal protocol <protocol-extensions>`,
etc. These features allow the development of rich terminal applications, such
as :doc:`Side-by-side diff <kittens/diff>` and :doc:`Unicode input
<kittens/unicode-input>`.
If you wish to support this mission and see the terminal ecosystem evolve,
consider donating so that I can devote more time to |kitty| development.
I have personally written `almost all kitty code
<https://github.com/kovidgoyal/kitty/graphs/contributors>`_.
You can choose to make either a one-time payment via PayPal or become a
*patron* of kitty development via the Patreon or Liberapay services
below:
.. raw:: html
:file: support.html

View File

@@ -15,6 +15,12 @@ from urllib.request import urlopen
os.chdir(os.path.dirname(os.path.abspath(__file__)))
non_characters = frozenset(range(0xfffe, 0x10ffff, 0x10000))
non_characters |= frozenset(range(0xffff, 0x10ffff + 1, 0x10000))
non_characters |= frozenset(range(0xfdd0, 0xfdf0))
if len(non_characters) != 66:
raise SystemExit('non_characters table incorrect')
def get_data(fname, folder='UCD'):
url = f'https://www.unicode.org/Public/{folder}/latest/{fname}'
@@ -189,11 +195,12 @@ def gen_emoji():
p('\treturn false;\n}')
def category_test(name, p, classes, comment, static=False):
def category_test(name, p, classes, comment, static=False, extra_chars=frozenset()):
static = 'static inline ' if static else ''
chars = set()
for c in classes:
chars |= class_maps[c]
chars |= extra_chars
p(f'{static}bool\n{name}(char_type code) {{')
p(f'\t// {comment} ({len(chars)} codepoints)' + ' {{' '{')
p('\tswitch(code) {')
@@ -245,7 +252,7 @@ def gen_ucd():
with create_header('kitty/unicode-data.c') as p:
p('#include "unicode-data.h"')
category_test('is_combining_char', p, {c for c in class_maps if c.startswith('M')}, 'M category (marks)')
category_test('is_ignored_char', p, 'Cc Cf Cs'.split(), 'Control characters (Cc Cf Cs)')
category_test('is_ignored_char', p, 'Cc Cf Cs'.split(), 'Control characters and non-characters', extra_chars=non_characters)
category_test('is_word_char', p, {c for c in class_maps if c[0] in 'LN'}, 'L and N categories')
category_test('is_CZ_category', p, cz, 'C and Z categories')
category_test('is_P_category', p, {c for c in class_maps if c[0] == 'P'}, 'P category (punctuation)')
@@ -258,7 +265,7 @@ def gen_ucd():
p('combining_type mark_for_codepoint(char_type c) {')
rmap = codepoint_to_mark_map(p, mark_map)
p('}\n')
if rmap[0xfe0e] != 1275:
if rmap[0xfe0e] != 1280:
raise ValueError('The mark for 0xfe0e has changed, you have to update VS15 to {} and VS16 to {} in unicode-data.h'.format(
rmap[0xfe0e], rmap[0xfe0f]
))
@@ -393,7 +400,7 @@ def gen_wcwidth():
add(p, 'East Asian double width', doublewidth, 2)
add(p, 'Emoji Presentation', emoji_categories['Emoji_Presentation'], 2)
add(p, 'Not assigned in the unicode character database', not_assigned, -1)
add(p, 'Not assigned in the unicode character database', not_assigned, -4)
p('\t\tdefault: return 1;')
p('\t}')

66
glfw/backend_utils.h vendored Normal file
View File

@@ -0,0 +1,66 @@
//========================================================================
// GLFW 3.3 Wayland - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
#pragma once
#include <unistd.h>
#include <poll.h>
#include <errno.h>
#ifdef __NetBSD__
#define ppoll pollts
#endif
static inline void
drainFd(int fd) {
static char drain_buf[64];
while(read(fd, drain_buf, sizeof(drain_buf)) < 0 && errno == EINTR);
}
static inline int
pollWithTimeout(struct pollfd *fds, nfds_t nfds, double timeout) {
const long seconds = (long) timeout;
const long nanoseconds = (long) ((timeout - seconds) * 1e9);
struct timespec tv = { seconds, nanoseconds };
return ppoll(fds, nfds, &tv, NULL);
}
static inline void
initPollData(struct pollfd *fds, int wakeup_fd, int display_fd) {
fds[0].fd = wakeup_fd; fds[1].fd = display_fd;
fds[0].events = POLLIN; fds[1].events = POLLIN;
}
static inline void
closeFds(int *fds, size_t count) {
while(count--) {
if (*fds > 0) {
close(*fds);
*fds = -1;
}
fds++;
}
}

View File

@@ -38,6 +38,7 @@ typedef void* id;
typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
typedef int (* GLFWcocoatextinputfilterfun)(int,int,int);
typedef int (* GLFWapplicationshouldhandlereopenfun)(int);
typedef struct VkMacOSSurfaceCreateInfoMVK
{

View File

@@ -482,6 +482,15 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
return NSTerminateCancel;
}
static GLFWapplicationshouldhandlereopenfun handle_reopen_callback = NULL;
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag
{
if (!handle_reopen_callback) return YES;
if (handle_reopen_callback(flag)) return YES;
return NO;
}
- (void)applicationDidChangeScreenParameters:(NSNotification *) notification
{
_GLFWwindow* window;
@@ -2088,3 +2097,102 @@ GLFWAPI GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow *hand
window->ns.textInputFilterCallback = callback;
return previous;
}
GLFWAPI GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback) {
GLFWapplicationshouldhandlereopenfun previous = handle_reopen_callback;
handle_reopen_callback = callback;
return previous;
}
GLFWAPI void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, unsigned short *cocoa_key, int *cocoa_mods) {
*cocoa_key = 0;
*cocoa_mods = 0;
if (glfw_mods & GLFW_MOD_SHIFT)
*cocoa_mods |= NSEventModifierFlagShift;
if (glfw_mods & GLFW_MOD_CONTROL)
*cocoa_mods |= NSEventModifierFlagControl;
if (glfw_mods & GLFW_MOD_ALT)
*cocoa_mods |= NSEventModifierFlagOption;
if (glfw_mods & GLFW_MOD_SUPER)
*cocoa_mods |= NSEventModifierFlagCommand;
if (glfw_mods & GLFW_MOD_CAPS_LOCK)
*cocoa_mods |= NSEventModifierFlagCapsLock;
switch(glfw_key) {
#define K(ch, name) case GLFW_KEY_##name: *cocoa_key = ch; break;
K('a', A);
K('b', B);
K('c', C);
K('d', D);
K('e', E);
K('f', F);
K('g', G);
K('h', H);
K('i', I);
K('j', J);
K('k', K);
K('l', L);
K('m', M);
K('n', N);
K('o', O);
K('p', P);
K('q', Q);
K('r', R);
K('s', S);
K('t', T);
K('u', U);
K('v', V);
K('w', W);
K('x', X);
K('y', Y);
K('z', Z);
K('0', 0);
K('1', 1);
K('2', 2);
K('3', 3);
K('5', 5);
K('6', 6);
K('7', 7);
K('8', 8);
K('9', 9);
K('\'', APOSTROPHE);
K(',', COMMA);
K('.', PERIOD);
K('/', SLASH);
K('-', MINUS);
K('=', EQUAL);
K(';', SEMICOLON);
K('[', LEFT_BRACKET);
K(']', RIGHT_BRACKET);
K('`', GRAVE_ACCENT);
K('\\', BACKSLASH);
K(0x35, ESCAPE);
K('\r', ENTER);
K('\t', TAB);
K(NSBackspaceCharacter, BACKSPACE);
K(NSInsertFunctionKey, INSERT);
K(NSDeleteCharacter, DELETE);
K(NSLeftArrowFunctionKey, LEFT);
K(NSRightArrowFunctionKey, RIGHT);
K(NSUpArrowFunctionKey, UP);
K(NSDownArrowFunctionKey, DOWN);
K(NSPageUpFunctionKey, PAGE_UP);
K(NSPageDownFunctionKey, PAGE_DOWN);
K(NSHomeFunctionKey, HOME);
K(NSEndFunctionKey, END);
K(NSPrintFunctionKey, PRINT_SCREEN);
case GLFW_KEY_F1 ... GLFW_KEY_F24:
*cocoa_key = NSF1FunctionKey + (glfw_key - GLFW_KEY_F1); break;
case GLFW_KEY_KP_0 ... GLFW_KEY_KP_9:
*cocoa_key = NSEventModifierFlagNumericPad | (0x52 + (glfw_key - GLFW_KEY_KP_0)); break;
K((unichar)(0x41|NSEventModifierFlagNumericPad), KP_DECIMAL);
K((unichar)(0x43|NSEventModifierFlagNumericPad), KP_MULTIPLY);
K((unichar)(0x45|NSEventModifierFlagNumericPad), KP_ADD);
K((unichar)(0x4B|NSEventModifierFlagNumericPad), KP_DIVIDE);
K((unichar)(0x4E|NSEventModifierFlagNumericPad), KP_SUBTRACT);
K((unichar)(0x51|NSEventModifierFlagNumericPad), KP_EQUAL);
#undef K
}
}

View File

@@ -43,7 +43,12 @@ def init_env(env, pkg_config, at_least_version, module='x11'):
else:
ans.ldpaths.extend('-lrt -lm -ldl'.split())
sinfo = json.load(open(os.path.join(base, 'source-info.json')))
ans.sources = sinfo['common']['sources'] + sinfo[module]['sources']
module_sources = list(sinfo[module]['sources'])
if module in ('x11', 'wayland'):
remove = 'linux_joystick.c' if is_bsd else 'null_joystick.c'
module_sources.remove(remove)
ans.sources = sinfo['common']['sources'] + module_sources
ans.all_headers = [x for x in os.listdir(base) if x.endswith('.h')]
if module in ('x11', 'wayland'):
@@ -93,13 +98,13 @@ def collect_source_information():
def extract_sources(group, start_pos=0):
for which in 'HEADERS SOURCES'.split():
yield which.lower(), filter(
yield which.lower(), list(filter(
lambda x: x[0] not in '"$',
re.search(
r'{0}_{1}\s+([^)]+?)[)]'.format(group, which),
raw[start_pos:]
).group(1).strip().split()
)
))
wayland_protocols = re.search(r'WaylandProtocols\s+(\S+)\s+', mraw).group(1)
wayland_protocols = list(map(int, wayland_protocols.split('.')))
@@ -107,13 +112,13 @@ def collect_source_information():
'common': dict(extract_sources('common')),
'wayland_protocols': wayland_protocols,
}
joystick = 'null' if is_bsd else 'linux'
for group in 'cocoa win32 x11 wayland osmesa'.split():
m = re.search('_GLFW_' + group.upper(), raw)
ans[group] = dict(extract_sources('glfw', m.start()))
if group in ('x11', 'wayland'):
ans[group]['headers'].append('{}_joystick.h'.format(joystick))
ans[group]['sources'].append('{}_joystick.c'.format(joystick))
for joystick in ('linux', 'null'):
ans[group]['headers'].append('{}_joystick.h'.format(joystick))
ans[group]['sources'].append('{}_joystick.c'.format(joystick))
if group == 'wayland':
ans[group]['protocols'] = p = []
for m in re.finditer(r'WAYLAND_PROTOCOLS_PKGDATADIR\}/(.+?)"?$', raw, flags=re.M):
@@ -197,6 +202,8 @@ def generate_wrappers(glfw_header, glfw_native_header):
void* glfwGetCocoaWindow(GLFWwindow* window)
uint32_t glfwGetCocoaMonitor(GLFWmonitor* monitor)
GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow* window, GLFWcocoatextinputfilterfun callback)
GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback)
void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, void* cocoa_key, void* cocoa_mods)
void* glfwGetX11Display(void)
int32_t glfwGetX11Window(GLFWwindow* window)
void glfwSetX11SelectionString(const char* string)
@@ -214,6 +221,8 @@ def generate_wrappers(glfw_header, glfw_native_header):
#include <stddef.h>
#include <stdint.h>
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int);
typedef int (* GLFWapplicationshouldhandlereopenfun)(int);
{}
{}
@@ -224,9 +233,9 @@ const char* load_glfw(const char* path);
f.write(header)
code = '''
#include <dlfcn.h>
#include "data-types.h"
#include "glfw-wrapper.h"
#include <dlfcn.h>
static void* handle = NULL;

20
glfw/glfw3.h vendored
View File

@@ -819,6 +819,12 @@ extern "C" {
* Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib).
*/
#define GLFW_HOVERED 0x0002000B
/*! @brief Input focus on calling show window hint and attribute
*
* Input focus [window hint](@ref GLFW_FOCUS_ON_SHOW_hint) or
* [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib).
*/
#define GLFW_FOCUS_ON_SHOW 0x0002000C
/*! @brief Framebuffer bit depth hint.
*
@@ -3057,6 +3063,11 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window);
* hidden. If the window is already visible or is in full screen mode, this
* function does nothing.
*
* By default, windowed mode windows are focused when shown
* Set the [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) window hint
* to change this behavior for all newly created windows, or change the
* behavior for an existing window with @ref glfwSetWindowAttrib.
*
* @param[in] window The window to make visible.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
@@ -3104,6 +3115,10 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window);
* initially created. Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to
* disable this behavior.
*
* Also by default, windowed mode windows are focused when shown
* with @ref glfwShowWindow. Set the
* [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) to disable this behavior.
*
* __Do not use this function__ to steal focus from other applications unless
* you are certain that is what the user wants. Focus stealing can be
* extremely disruptive.
@@ -3301,8 +3316,9 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib);
*
* The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib),
* [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib),
* [GLFW_FLOATING](@ref GLFW_FLOATING_attrib) and
* [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib).
* [GLFW_FLOATING](@ref GLFW_FLOATING_attrib),
* [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and
* [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib).
*
* Some of these attributes are ignored for full screen windows. The new
* value will take effect if the window is later made windowed.

2
glfw/internal.h vendored
View File

@@ -267,6 +267,7 @@ struct _GLFWwndconfig
GLFWbool floating;
GLFWbool maximized;
GLFWbool centerCursor;
GLFWbool focusOnShow;
struct {
GLFWbool retina;
char frameName[256];
@@ -372,6 +373,7 @@ struct _GLFWwindow
GLFWbool decorated;
GLFWbool autoIconify;
GLFWbool floating;
GLFWbool focusOnShow;
GLFWbool shouldClose;
void* userPointer;
GLFWvidmode videoMode;

View File

@@ -1,132 +1,138 @@
{
"cocoa": {
"headers": [
"cocoa_platform.h",
"cocoa_joystick.h",
"posix_thread.h",
"nsgl_context.h",
"egl_context.h",
"cocoa_platform.h",
"cocoa_joystick.h",
"posix_thread.h",
"nsgl_context.h",
"egl_context.h",
"osmesa_context.h"
],
],
"sources": [
"cocoa_init.m",
"cocoa_joystick.m",
"cocoa_monitor.m",
"cocoa_window.m",
"cocoa_time.c",
"posix_thread.c",
"nsgl_context.m",
"egl_context.c",
"cocoa_init.m",
"cocoa_joystick.m",
"cocoa_monitor.m",
"cocoa_window.m",
"cocoa_time.c",
"posix_thread.c",
"nsgl_context.m",
"egl_context.c",
"osmesa_context.c"
]
},
},
"common": {
"headers": [
"internal.h",
"internal.h",
"mappings.h"
],
],
"sources": [
"context.c",
"init.c",
"input.c",
"monitor.c",
"vulkan.c",
"context.c",
"init.c",
"input.c",
"monitor.c",
"vulkan.c",
"window.c"
]
},
},
"osmesa": {
"headers": [
"null_platform.h",
"null_joystick.h",
"posix_time.h",
"posix_thread.h",
"null_platform.h",
"null_joystick.h",
"posix_time.h",
"posix_thread.h",
"osmesa_context.h"
],
],
"sources": [
"null_init.c",
"null_monitor.c",
"null_window.c",
"null_joystick.c",
"posix_time.c",
"posix_thread.c",
"null_init.c",
"null_monitor.c",
"null_window.c",
"null_joystick.c",
"posix_time.c",
"posix_thread.c",
"osmesa_context.c"
]
},
},
"wayland": {
"headers": [
"wl_platform.h",
"posix_time.h",
"posix_thread.h",
"xkb_glfw.h",
"egl_context.h",
"osmesa_context.h",
"linux_joystick.h"
],
"wl_platform.h",
"posix_time.h",
"posix_thread.h",
"xkb_glfw.h",
"backend_utils.h",
"egl_context.h",
"osmesa_context.h",
"linux_joystick.h",
"null_joystick.h"
],
"protocols": [
"stable/xdg-shell/xdg-shell.xml",
"stable/viewporter/viewporter.xml",
"unstable/relative-pointer/relative-pointer-unstable-v1.xml",
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
"stable/xdg-shell/xdg-shell.xml",
"stable/viewporter/viewporter.xml",
"unstable/relative-pointer/relative-pointer-unstable-v1.xml",
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
],
],
"sources": [
"wl_init.c",
"wl_monitor.c",
"wl_window.c",
"posix_time.c",
"posix_thread.c",
"xkb_glfw.c",
"egl_context.c",
"osmesa_context.c",
"linux_joystick.c"
"wl_init.c",
"wl_monitor.c",
"wl_window.c",
"posix_time.c",
"posix_thread.c",
"xkb_glfw.c",
"egl_context.c",
"osmesa_context.c",
"linux_joystick.c",
"null_joystick.c"
]
},
},
"wayland_protocols": [
1,
1,
12
],
],
"win32": {
"headers": [
"win32_platform.h",
"win32_joystick.h",
"wgl_context.h",
"egl_context.h",
"win32_platform.h",
"win32_joystick.h",
"wgl_context.h",
"egl_context.h",
"osmesa_context.h"
],
],
"sources": [
"win32_init.c",
"win32_joystick.c",
"win32_monitor.c",
"win32_time.c",
"win32_thread.c",
"win32_window.c",
"wgl_context.c",
"egl_context.c",
"win32_init.c",
"win32_joystick.c",
"win32_monitor.c",
"win32_time.c",
"win32_thread.c",
"win32_window.c",
"wgl_context.c",
"egl_context.c",
"osmesa_context.c"
]
},
},
"x11": {
"headers": [
"x11_platform.h",
"xkb_glfw.h",
"posix_time.h",
"posix_thread.h",
"glx_context.h",
"egl_context.h",
"osmesa_context.h",
"linux_joystick.h"
],
"x11_platform.h",
"xkb_glfw.h",
"backend_utils.h",
"posix_time.h",
"posix_thread.h",
"glx_context.h",
"egl_context.h",
"osmesa_context.h",
"linux_joystick.h",
"null_joystick.h"
],
"sources": [
"x11_init.c",
"x11_monitor.c",
"x11_window.c",
"xkb_glfw.c",
"posix_time.c",
"posix_thread.c",
"glx_context.c",
"egl_context.c",
"osmesa_context.c",
"linux_joystick.c"
"x11_init.c",
"x11_monitor.c",
"x11_window.c",
"xkb_glfw.c",
"posix_time.c",
"posix_thread.c",
"glx_context.c",
"egl_context.c",
"osmesa_context.c",
"linux_joystick.c",
"null_joystick.c"
]
}
}

13
glfw/window.c vendored
View File

@@ -201,6 +201,7 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
window->decorated = wndconfig.decorated;
window->autoIconify = wndconfig.autoIconify;
window->floating = wndconfig.floating;
window->focusOnShow = wndconfig.focusOnShow;
window->cursorMode = GLFW_CURSOR_NORMAL;
window->minwidth = GLFW_DONT_CARE;
@@ -267,6 +268,7 @@ void glfwDefaultWindowHints(void)
_glfw.hints.window.focused = GLFW_TRUE;
_glfw.hints.window.autoIconify = GLFW_TRUE;
_glfw.hints.window.centerCursor = GLFW_TRUE;
_glfw.hints.window.focusOnShow = GLFW_TRUE;
// The default is 24 bits of color, 24 bits of depth and 8 bits of stencil,
// double buffered
@@ -370,6 +372,9 @@ GLFWAPI void glfwWindowHint(int hint, int value)
case GLFW_CENTER_CURSOR:
_glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE;
return;
case GLFW_FOCUS_ON_SHOW:
_glfw.hints.window.focusOnShow = value ? GLFW_TRUE : GLFW_FALSE;
return;
case GLFW_CLIENT_API:
_glfw.hints.context.client = value;
return;
@@ -755,7 +760,9 @@ GLFWAPI void glfwShowWindow(GLFWwindow* handle)
return;
_glfwPlatformShowWindow(window);
_glfwPlatformFocusWindow(window);
if (window->focusOnShow)
_glfwPlatformFocusWindow(window);
}
GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle)
@@ -820,6 +827,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
return _glfwPlatformWindowMaximized(window);
case GLFW_HOVERED:
return _glfwPlatformWindowHovered(window);
case GLFW_FOCUS_ON_SHOW:
return window->focusOnShow;
case GLFW_TRANSPARENT_FRAMEBUFFER:
return _glfwPlatformFramebufferTransparent(window);
case GLFW_RESIZABLE:
@@ -896,6 +905,8 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value)
if (!window->monitor)
_glfwPlatformSetWindowFloating(window, value);
}
else if (attrib == GLFW_FOCUS_ON_SHOW)
window->focusOnShow = value;
else
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib);
}

12
glfw/wl_init.c vendored
View File

@@ -24,7 +24,9 @@
//
//========================================================================
#define _GNU_SOURCE
#include "internal.h"
#include "backend_utils.h"
#include <assert.h>
#include <linux/input.h>
@@ -33,6 +35,7 @@
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <wayland-client.h>
@@ -615,6 +618,13 @@ static const struct wl_registry_listener registryListener = {
int _glfwPlatformInit(void)
{
if (pipe2(_glfw.wl.eventLoopData.wakeupFds, O_CLOEXEC | O_NONBLOCK) != 0)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Wayland: failed to create self pipe");
return GLFW_FALSE;
}
_glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0");
if (!_glfw.wl.cursor.handle)
{
@@ -654,6 +664,7 @@ int _glfwPlatformInit(void)
"Wayland: Failed to connect to display");
return GLFW_FALSE;
}
initPollData(_glfw.wl.eventLoopData.fds, _glfw.wl.eventLoopData.wakeupFds[0], wl_display_get_fd(_glfw.wl.display));
_glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
wl_registry_add_listener(_glfw.wl.registry, &registryListener, NULL);
@@ -744,6 +755,7 @@ void _glfwPlatformTerminate(void)
wl_display_flush(_glfw.wl.display);
wl_display_disconnect(_glfw.wl.display);
}
closeFds(_glfw.wl.eventLoopData.wakeupFds, sizeof(_glfw.wl.eventLoopData.wakeupFds)/sizeof(_glfw.wl.eventLoopData.wakeupFds[0]));
}
const char* _glfwPlatformGetVersionString(void)

6
glfw/wl_platform.h vendored
View File

@@ -26,6 +26,7 @@
#include <wayland-client.h>
#include <dlfcn.h>
#include <poll.h>
typedef VkFlags VkWaylandSurfaceCreateFlagsKHR;
@@ -228,6 +229,11 @@ typedef struct _GLFWlibraryWayland
PFN_wl_egl_window_resize window_resize;
} egl;
struct {
struct pollfd fds[2];
int wakeupFds[2];
} eventLoopData;
} _GLFWlibraryWayland;
// Wayland-specific per-monitor data

41
glfw/wl_window.c vendored
View File

@@ -27,15 +27,14 @@
#define _GNU_SOURCE
#include "internal.h"
#include "backend_utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <poll.h>
static void handlePing(void* data,
@@ -700,21 +699,20 @@ dispatchPendingKeyRepeats() {
}
}
static int
adjustTimeoutForKeyRepeat(int timeout) {
static double
adjustTimeoutForKeyRepeat(double timeout) {
if (_glfw.wl.keyRepeatInfo.nextRepeatAt <= 0 || _glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return timeout;
double now = glfwGetTime();
if (timeout < 0 || now + timeout / 1000. > _glfw.wl.keyRepeatInfo.nextRepeatAt) {
timeout = _glfw.wl.keyRepeatInfo.nextRepeatAt <= now ? 0 : ( (_glfw.wl.keyRepeatInfo.nextRepeatAt - now) * 1000 + 1 );
if (timeout < 0 || now + timeout > _glfw.wl.keyRepeatInfo.nextRepeatAt) {
timeout = _glfw.wl.keyRepeatInfo.nextRepeatAt <= now ? 0 : ( (_glfw.wl.keyRepeatInfo.nextRepeatAt - now) + 0.001 );
}
return timeout;
}
static void
handleEvents(int timeout)
handleEvents(double timeout)
{
struct wl_display* display = _glfw.wl.display;
struct pollfd pfd = { wl_display_get_fd(display), POLLIN };
while (wl_display_prepare_read(display) != 0)
wl_display_dispatch_pending(display);
@@ -736,19 +734,25 @@ handleEvents(int timeout)
dispatchPendingKeyRepeats();
timeout = adjustTimeoutForKeyRepeat(timeout);
GLFWbool read_ok = GLFW_FALSE;
for (nfds_t i = 0; i < 2; i++) _glfw.wl.eventLoopData.fds[i].revents = 0;
if (poll(&pfd, 1, timeout) > 0)
{
if (pfd.revents & POLLIN)
if (timeout >= 0) {
const int result = pollWithTimeout(_glfw.wl.eventLoopData.fds, 2, timeout);
if (result > 0)
{
wl_display_read_events(display);
wl_display_dispatch_pending(display);
if (_glfw.wl.eventLoopData.fds[0].revents & POLLIN) drainFd(_glfw.wl.eventLoopData.fds[0].fd);
read_ok = _glfw.wl.eventLoopData.fds[1].revents & POLLIN;
}
else
{
wl_display_cancel_read(display);
} else {
if (poll(_glfw.wl.eventLoopData.fds, 2, -1) > 0) {
if (_glfw.wl.eventLoopData.fds[0].revents & POLLIN) drainFd(_glfw.wl.eventLoopData.fds[0].fd);
read_ok = _glfw.wl.eventLoopData.fds[1].revents & POLLIN;
}
}
if (read_ok) {
wl_display_read_events(display);
wl_display_dispatch_pending(display);
}
else
{
@@ -1206,12 +1210,13 @@ void _glfwPlatformWaitEvents(void)
void _glfwPlatformWaitEventsTimeout(double timeout)
{
handleEvents((int) (timeout * 1e3));
handleEvents(timeout);
}
void _glfwPlatformPostEmptyEvent(void)
{
wl_display_sync(_glfw.wl.display);
while (write(_glfw.wl.eventLoopData.wakeupFds[1], "w", 1) < 0 && errno == EINTR);
}
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)

38
glfw/x11_init.c vendored
View File

@@ -25,7 +25,9 @@
//
//========================================================================
#define _GNU_SOURCE
#include "internal.h"
#include "backend_utils.h"
#include <X11/Xresource.h>
@@ -34,6 +36,7 @@
#include <limits.h>
#include <stdio.h>
#include <locale.h>
#include <fcntl.h>
// Check whether the specified atom is supported
@@ -454,7 +457,7 @@ static GLFWbool initExtensions(void)
// Retrieve system content scale via folklore heuristics
//
static void getSystemContentScale(float* xscale, float* yscale)
void _glfwGetSystemContentScaleX11(float* xscale, float* yscale, GLFWbool bypass_cache)
{
// NOTE: Default to the display-wide DPI as we don't currently have a policy
// for which monitor a window is considered to be on
@@ -466,7 +469,20 @@ static void getSystemContentScale(float* xscale, float* yscale)
// NOTE: Basing the scale on Xft.dpi where available should provide the most
// consistent user experience (matches Qt, Gtk, etc), although not
// always the most accurate one
char* rms = XResourceManagerString(_glfw.x11.display);
char* rms = NULL;
char* owned_rms = NULL;
if (bypass_cache)
{
_glfwGetWindowPropertyX11(_glfw.x11.root,
_glfw.x11.RESOURCE_MANAGER,
XA_STRING,
(unsigned char**) &owned_rms);
rms = owned_rms;
} else {
rms = XResourceManagerString(_glfw.x11.display);
}
if (rms)
{
XrmDatabase db = XrmGetStringDatabase(rms);
@@ -483,6 +499,7 @@ static void getSystemContentScale(float* xscale, float* yscale)
XrmDestroyDatabase(db);
}
XFree(owned_rms);
}
*xscale = xdpi / 96.f;
@@ -589,7 +606,6 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot)
return cursor;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
@@ -599,6 +615,13 @@ int _glfwPlatformInit(void)
XInitThreads();
XrmInitialize();
if (pipe2(_glfw.x11.eventLoopData.wakeupFds, O_CLOEXEC | O_NONBLOCK) != 0)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"X11: failed to create self pipe");
return GLFW_FALSE;
}
_glfw.x11.display = XOpenDisplay(NULL);
if (!_glfw.x11.display)
{
@@ -617,11 +640,16 @@ int _glfwPlatformInit(void)
return GLFW_FALSE;
}
initPollData(_glfw.x11.eventLoopData.fds, _glfw.x11.eventLoopData.wakeupFds[0], ConnectionNumber(_glfw.x11.display));
_glfw.x11.eventLoopData.fds[2].events = POLLIN;
_glfw.x11.screen = DefaultScreen(_glfw.x11.display);
_glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen);
_glfw.x11.context = XUniqueContext();
_glfw.x11.RESOURCE_MANAGER = XInternAtom(_glfw.x11.display, "RESOURCE_MANAGER", True);
XSelectInput(_glfw.x11.display, _glfw.x11.root, PropertyChangeMask);
getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY);
_glfwGetSystemContentScaleX11(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY, GLFW_FALSE);
if (!initExtensions())
return GLFW_FALSE;
@@ -668,6 +696,7 @@ void _glfwPlatformTerminate(void)
{
XCloseDisplay(_glfw.x11.display);
_glfw.x11.display = NULL;
_glfw.x11.eventLoopData.fds[0].fd = -1;
}
if (_glfw.x11.xcursor.handle)
@@ -714,6 +743,7 @@ void _glfwPlatformTerminate(void)
#if defined(__linux__)
_glfwTerminateJoysticksLinux();
#endif
closeFds(_glfw.x11.eventLoopData.wakeupFds, sizeof(_glfw.x11.eventLoopData.wakeupFds)/sizeof(_glfw.x11.eventLoopData.wakeupFds[0]));
}
const char* _glfwPlatformGetVersionString(void)

11
glfw/x11_platform.h vendored
View File

@@ -29,6 +29,7 @@
#include <signal.h>
#include <stdint.h>
#include <dlfcn.h>
#include <poll.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
@@ -282,6 +283,9 @@ typedef struct _GLFWlibraryX11
Atom ATOM_PAIR;
Atom GLFW_SELECTION;
// XRM database atom
Atom RESOURCE_MANAGER;
struct {
GLFWbool available;
void* handle;
@@ -378,6 +382,11 @@ typedef struct _GLFWlibraryX11
PFN_XRenderFindVisualFormat FindVisualFormat;
} xrender;
struct {
struct pollfd fds[3];
int wakeupFds[2];
} eventLoopData;
} _GLFWlibraryX11;
// X11-specific per-monitor data
@@ -402,7 +411,6 @@ typedef struct _GLFWcursorX11
} _GLFWcursorX11;
void _glfwPollMonitorsX11(void);
void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired);
void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor);
@@ -419,4 +427,5 @@ void _glfwGrabErrorHandlerX11(void);
void _glfwReleaseErrorHandlerX11(void);
void _glfwInputErrorX11(int error, const char* message);
void _glfwGetSystemContentScaleX11(float* xscale, float* yscale, GLFWbool bypass_cache);
void _glfwPushSelectionToManagerX11(void);

81
glfw/x11_window.c vendored
View File

@@ -25,13 +25,13 @@
//
//========================================================================
#define _GNU_SOURCE
#include "internal.h"
#include "backend_utils.h"
#include <X11/cursorfont.h>
#include <X11/Xmd.h>
#include <sys/select.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -50,50 +50,52 @@
#define _GLFW_XDND_VERSION 5
// Wait for data to arrive using select
// Wait for data to arrive using poll
// This avoids blocking other threads via the per-display Xlib lock that also
// covers GLX functions
//
static GLFWbool waitForEvent(double* timeout)
{
fd_set fds;
const int fd = ConnectionNumber(_glfw.x11.display);
int count = fd + 1;
nfds_t count = 2;
#if defined(__linux__)
if (_glfw.linjs.inotify > fd)
count = _glfw.linjs.inotify + 1;
if (_glfw.linjs.inotify > 0)
{
count = 3;
_glfw.x11.eventLoopData.fds[2].fd = _glfw.linjs.inotify;
}
#endif
for (;;)
{
FD_ZERO(&fds);
FD_SET(fd, &fds);
#if defined(__linux__)
if (_glfw.linjs.inotify > 0)
FD_SET(_glfw.linjs.inotify, &fds);
#endif
for (nfds_t i = 0; i < count; i++) _glfw.x11.eventLoopData.fds[i].revents = 0;
if (timeout)
{
const long seconds = (long) *timeout;
const long microseconds = (long) ((*timeout - seconds) * 1e6);
struct timeval tv = { seconds, microseconds };
const uint64_t base = _glfwPlatformGetTimerValue();
const int result = select(count, &fds, NULL, NULL, &tv);
const int error = errno;
const int result = pollWithTimeout(_glfw.x11.eventLoopData.fds, count, *timeout);
*timeout -= (_glfwPlatformGetTimerValue() - base) /
(double) _glfwPlatformGetTimerFrequency();
if (result > 0)
{
if (_glfw.x11.eventLoopData.fds[0].revents & POLLIN) drainFd(_glfw.x11.eventLoopData.fds[0].fd);
return GLFW_TRUE;
if ((result == -1 && error == EINTR) || *timeout <= 0.0)
}
if (result == 0)
return GLFW_FALSE;
if (*timeout > 0 && (errno == EINTR || errno == EAGAIN)) continue;
return GLFW_FALSE;
}
else {
const int result = poll(_glfw.x11.eventLoopData.fds, count, -1);
if (result > 0)
{
if (_glfw.x11.eventLoopData.fds[0].revents & POLLIN) drainFd(_glfw.x11.eventLoopData.fds[0].fd);
return GLFW_TRUE;
}
if (result == 0)
return GLFW_FALSE;
if (errno != EINTR && errno != EAGAIN) return GLFW_FALSE;
}
else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR)
return GLFW_TRUE;
}
}
@@ -1142,6 +1144,24 @@ static void releaseMonitor(_GLFWwindow* window)
}
}
static void onConfigChange()
{
float xscale, yscale;
_glfwGetSystemContentScaleX11(&xscale, &yscale, GLFW_TRUE);
if (xscale != _glfw.x11.contentScaleX || yscale != _glfw.x11.contentScaleY)
{
_GLFWwindow* window = _glfw.windowListHead;
_glfw.x11.contentScaleX = xscale;
_glfw.x11.contentScaleY = yscale;
while (window)
{
_glfwInputWindowContentScale(window, xscale, yscale);
window = window->next;
}
}
}
// Process the specified X event
//
static void processEvent(XEvent *event)
@@ -1159,6 +1179,14 @@ static void processEvent(XEvent *event)
}
}
if (event->type == PropertyNotify &&
event->xproperty.window == _glfw.x11.root &&
event->xproperty.atom == _glfw.x11.RESOURCE_MANAGER)
{
onConfigChange();
return;
}
if (event->type == GenericEvent)
{
if (_glfw.x11.xi.available)
@@ -2597,6 +2625,7 @@ void _glfwPlatformPostEmptyEvent(void)
XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event);
XFlush(_glfw.x11.display);
while (write(_glfw.x11.eventLoopData.wakeupFds[1], "w", 1) < 0 && errno == EINTR);
}
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)

36
glfw/xkb_glfw.c vendored
View File

@@ -181,6 +181,10 @@ glfw_xkb_release(_GLFWXKBData *xkb) {
xkb_keymap_unref(xkb->keymap);
xkb->keymap = NULL;
}
if (xkb->default_keymap) {
xkb_keymap_unref(xkb->default_keymap);
xkb->default_keymap = NULL;
}
if (xkb->state) {
xkb_state_unref(xkb->state);
xkb->state = NULL;
@@ -189,6 +193,10 @@ glfw_xkb_release(_GLFWXKBData *xkb) {
xkb_state_unref(xkb->clean_state);
xkb->clean_state = NULL;
}
if (xkb->default_state) {
xkb_state_unref(xkb->default_state);
xkb->default_state = NULL;
}
if (xkb->context) {
xkb_context_unref(xkb->context);
xkb->context = NULL;
@@ -272,6 +280,21 @@ glfw_xkb_compile_keymap(_GLFWXKBData *xkb, const char *map_str) {
xkb->modifiers = 0;
xkb->activeUnknownModifiers = 0;
}
if (!xkb->default_keymap && xkb->context) {
// The system default keymap, can be overridden by the XKB_DEFAULT_RULES
// env var, see
// https://xkbcommon.org/doc/current/structxkb__rule__names.html
static struct xkb_rule_names default_rule_names = {0};
xkb->default_keymap = xkb_keymap_new_from_names(xkb->context, &default_rule_names, XKB_KEYMAP_COMPILE_NO_FLAGS);
if (xkb->default_keymap) {
xkb->default_state = xkb_state_new(keymap);
if (!xkb->default_state) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create default XKB state, fallback key processing is unavailable");
}
} else {
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create default XKB keymap, fallback key processing is unavailable");
}
}
return ok;
}
@@ -379,7 +402,7 @@ format_xkb_mods(_GLFWXKBData *xkb, const char* name, xkb_mod_mask_t mods) {
void
glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t scancode, int action) {
const xkb_keysym_t *syms, *clean_syms;
const xkb_keysym_t *syms, *clean_syms, *default_syms;
xkb_keysym_t glfw_sym;
xkb_keycode_t code_for_sym = scancode;
#ifdef _GLFW_WAYLAND
@@ -422,6 +445,15 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t
if (text[0]) { debug("%s: %s ", text_type, text); }
}
int glfw_keycode = glfw_key_for_sym(glfw_sym);
debug("%sglfw_key: %s\n", format_mods(xkb->modifiers), _glfwGetKeyName(glfw_keycode));
if (glfw_keycode == GLFW_KEY_UNKNOWN && !text[0]) {
int num_default_syms = xkb_state_key_get_syms(xkb->default_state, code_for_sym, &default_syms);
if (num_default_syms > 0) {
glfw_sym = default_syms[0];
glfw_keycode = glfw_key_for_sym(glfw_sym);
}
debug("%sglfw_fallback_key: %s\n", format_mods(xkb->modifiers), _glfwGetKeyName(glfw_keycode));
} else {
debug("%sglfw_key: %s\n", format_mods(xkb->modifiers), _glfwGetKeyName(glfw_keycode));
}
_glfwInputKeyboard(window, glfw_keycode, glfw_sym, action, xkb->modifiers, text, 0);
}

2
glfw/xkb_glfw.h vendored
View File

@@ -35,8 +35,10 @@
typedef struct {
struct xkb_context* context;
struct xkb_keymap* keymap;
struct xkb_keymap* default_keymap;
struct xkb_state* state;
struct xkb_state* clean_state;
struct xkb_state* default_state;
struct xkb_compose_state* composeState;
xkb_mod_index_t controlIdx;

View File

@@ -1,386 +1 @@
= The terminal graphics protocol
:toc:
:toc-placement!:
The goal of this specification is to create a flexible and performant protocol
that allows the program running in the terminal, hereafter called the _client_,
to render arbitrary pixel (raster) graphics to the screen of the terminal
emulator. The major design goals are
* Should not require terminal emulators to understand image formats.
* Should allow specifying graphics to be drawn at individual pixel positions.
* The graphics should integrate with the text, in particular it should be possible to draw graphics
below as well as above the text, with alpha blending. The graphics should also scroll with the text, automatically.
* Should use optimizations when the client is running on the same computer as the terminal emulator.
For some discussion regarding the design choices, see link:../../issues/33[#33].
To see a quick demo, inside a kitty terminal run:
```
kitty icat path/to/some/image.png
```
You can also see a screenshot with more sophisticated features such as alpha-blending and text over graphics
link:https://github.com/kovidgoyal/kitty/issues/33#issuecomment-334436100[here].
Some programs that use the kitty graphics protocol:
* link:https://github.com/dsanson/termpdf[termdpf] - a terminal PDF/DJVU/CBR viewer
* link:https://github.com/ranger/ranger[ranger] - a terminal file manager, with image previews, see this link:https://github.com/ranger/ranger/pull/1077[PR]
* link:https://github.com/kovidgoyal/kitty/tree/master/kittens/diff[kitty-diff] - a side-by-side terminal diff program with support for images
toc::[]
== Getting the window size
In order to know what size of images to display and how to position them, the client must be able to get the
window size in pixels and the number of cells per row and column. This can be done by using the `TIOCGWINSZ` ioctl.
Some code to demonstrate its use
In C:
```C
struct ttysize ts;
ioctl(0, TIOCGWINSZ, &ts);
printf("number of columns: %i, number of rows: %i, screen width: %i, screen height: %i\n", sz.ws_col, sz.ws_row, sz.ws_xpixel, sz.ws_ypixel);
```
In Python:
```py
import array, fcntl, termios
buf = array.array('H', [0, 0, 0, 0])
fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, buf)
print('number of columns: {}, number of rows: {}, screen width: {}, screen height: {}'.format(*buf))
```
Note that some terminals return `0` for the width and height values. Such terminals should be modified to return the correct values.
Examples of terminals that return correct values: `kitty, xterm`
== The graphics escape code
All graphics escape codes are of the form:
```
<ESC>_G<control data>;<payload><ESC>\
```
This is a so-called _Application Programming Command (APC)_. Most terminal
emulators ignore APC codes, making it safe to use.
The control data is a comma-separated list of `key=value` pairs. The payload
is arbitrary binary data, base64-encoded to prevent interoperation problems
with legacy terminals that get confused by control codes within an APC code.
The meaning of the payload is interpreted based on the control data.
The first step is to transmit the actual image data.
== Transferring pixel data
The first consideration when transferring data between the client and the
terminal emulator is the format in which to do so. Since there is a vast and
growing number of image formats in existence, it does not make sense to have
every terminal emulator implement support for them. Instead, the client should
send simple pixel data to the terminal emulator. The obvious downside to this
is performance, especially when the client is running on a remote machine.
Techniques for remedying this limitation are discussed later. The terminal
emulator must understand pixel data in three formats, 24-bit RGB, 32-bit RGBA and
PNG. This is specified using the `f` key in the control data. `f=32` (which is the
default) indicates 32-bit RGBA data and `f=24` indicates 24-bit RGB data and `f=100`
indicates PNG data. The PNG format is supported for convenience and a compact way
of transmitting paletted images.
=== RGB and RGBA data
In these formats the pixel data is stored directly as 3 or 4 bytes per pixel, respectively.
When specifying images in this format, the image dimensions **must** be sent in the control data.
For example:
```
<ESC>_Gf=24,s=10,v=20;<payload><ESC>\
```
Here the width and height are specified using the `s` and `v` keys respectively. Since
`f=24` there are three bytes per pixel and therefore the pixel data must be `3 * 10 * 20 = 600`
bytes.
=== PNG data
In this format any PNG image can be transmitted directly. For example:
```
<ESC>_Gf=100;<payload><ESC>\
```
The PNG format is specified using the `f=100` key. The width and height of
the image will be read from the PNG data itself. Note that if you use both PNG and
compression, then you must provide the `S` key with the size of the PNG data.
=== Compression
The client can send compressed image data to the terminal emulator, by specifying the
`o` key. Currently, only zlib based deflate compression is supported, which is specified using
`o=z`. For example,
```
<ESC>_Gf=24,s=10,v=20,o=z;<payload><ESC>\
```
This is the same as the example from the RGB data section, except that the
payload is now compressed using deflate. The terminal emulator will decompress
it before rendering. You can specify compression for any format. The terminal
emulator will decompress before interpreting the pixel data.
=== The transmission medium
The transmission medium is specified using the `t` key. The `t` key defaults to `d`
and can take the values:
|===
| Value of `t` | Meaning
| d | Direct (the data is transmitted within the escape code itself)
| f | A simple file
| t | A temporary file, the terminal emulator will delete the file after reading the pixel data
| s | A http://man7.org/linux/man-pages/man7/shm_overview.7.html[POSIX shared memory object]. The terminal emulator will delete it after reading the pixel data
|===
==== Local client
First let us consider the local client techniques (files and shared memory). Some examples:
```
<ESC>_Gf=100,t=f;<encoded /path/to/file.png><ESC>\
```
Here we tell the terminal emulator to read PNG data from the specified file of
the specified size.
```
<ESC>_Gs=10,v=2,t=s,o=z;<encoded /some-shared-memory-name><ESC>\
```
Here we tell the terminal emulator to read compressed image data from
the specified shared memory object.
The client can also specify a size and offset to tell the terminal emulator
to only read a part of the specified file. The is done using the `S` and `O`
keys respectively. For example:
```
<ESC>_Gs=10,v=2,t=s,S=80,O=10;<encoded /some-shared-memory-name><ESC>\
```
This tells the terminal emulator to read `80` bytes starting from the offset `10`
inside the specified shared memory buffer.
==== Remote client
Remote clients, those that are unable to use the filesystem/shared memory to
transmit data, must send the pixel data directly using escape codes. Since
escape codes are of limited maximum length, the data will need to be chunked up
for transfer. This is done using the `m` key. The pixel data must first be
base64 encoded then chunked up into chunks no larger than `4096` bytes. The client
then sends the graphics escape code as usual, with the addition of an `m` key that
must have the value `1` for all but the last chunk, where it must be `0`. For example,
if the data is split into three chunks, the client would send the following
sequence of escape codes to the terminal emulator:
```
<ESC>_Gs=100,v=30,m=1;<encoded pixel data first chunk><ESC>\
<ESC>_Gm=1;<encoded pixel data second chunk><ESC>\
<ESC>_Gm=0;<encoded pixel data last chunk><ESC>\
```
Note that only the first escape code needs to have the full set of control
codes such as width, height, format etc. Subsequent chunks must have
only the `m` key. The client **must** finish sending all chunks for a single image
before sending any other graphics related escape codes.
=== Detecting available transmission mediums
Since a client has no a-priori knowledge of whether it shares a filesystem/shared memory
with the terminal emulator, it can send an id with the control data, using the `i` key
(which can be an arbitrary positive integer up to 4294967295, it must not be zero).
If it does so, the terminal emulator will reply after trying to load the image, saying
whether loading was successful or not. For example:
```
<ESC>_Gi=31,s=10,v=2,t=s;<encoded /some-shared-memory-name><ESC>\
```
to which the terminal emulator will reply (after trying to load the data):
```
<ESC>_Gi=31;error message or OK<ESC>\
```
Here the `i` value will be the same as was sent by the client in the original
request. The message data will be a ASCII encoded string containing only
printable characters and spaces. The string will be `OK` if reading the pixel
data succeeded or an error message.
Sometimes, using an id is not appropriate, for example, if you do not want to
replace a previously sent image with the same id, or if you are sending a dummy
image and do not want it stored by the terminal emulator. In that case, you can
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
replace an existing image with the same id, nor will it store the image.
== Display images on screen
Every transmitted image can be displayed an arbitrary number of times on the
screen, in different locations, using different parts of the source image, as
needed. You can either simultaneously transmit and display an image using the
action `a=T`, or first transmit the image with a id, such as `i=10` and then display
it with `a=p,i=10` which will display the previously transmitted image at the current
cursor position. When specifying an image id, the terminal emulator will reply with an
acknowledgement code, which will be either:
```
<ESC>_Gi=<id>;OK<ESC>\
```
when the image referred to by id was found, or
```
<ESC>_Gi=<id>;ENOENT:<some detailed error msg><ESC>\
```
when the image with the specified id was not found. This is similar to the
scheme described above for querying available transmission media, except that
here we are querying if the image with the specified id is available or needs to
be re-transmitted.
=== Controlling displayed image layout
The image is rendered at the current cursor position, from the upper left corner of
the current cell. You can also specify extra `X=3` and `Y=4` pixel offsets to display from
a different origin within the cell. Note that the offsets must be smaller that the size of the cell.
By default, the entire image will be displayed (images wider than the available
width will be truncated on the right edge). You can choose a source rectangle (in pixels)
as the part of the image to display. This is done with the keys: `x, y, w, h` which specify
the top-left corner, width and height of the source rectangle.
You can also ask the terminal emulator to display the image in a specified rectangle
(num of columns / num of lines), using the control codes `c,r`. `c` is the number of columns
and `r` the number of rows. The image will be scaled (enlarged/shrunk) as needed to fit
the specified area. Note that if you specify a start cell offset via the `X,Y` keys, it is not
added to the number of rows/columns.
Finally, you can specify the image *z-index*, i.e. the vertical stacking order. Images
placed in the same location with different z-index values will be blended if
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
allows rendering of text on top of images.
== Deleting images
Images can be deleted by using the delete action `a=d`. If specified without any
other keys, it will delete all images visible on screen. To delete specific images,
use the `d` key as described in the table below. Note that each value of d has
both a lowercase and an uppercase variant. The lowercase variant only deletes the
images without necessarily freeing up the stored image data, so that the images can be
re-displayed without needing to resend the data. The uppercase variants will delete
the image data as well, provided that the image is not referenced elsewhere, such as in the
scrollback buffer. The values of the `x` and `y` keys are the same as cursor positions (i.e.
x=1, y=1 is the top left cell).
|===
| Value of `d` | Meaning
| `a` or `A` | Delete all images visible on screen
| `i` or `I` | Delete all images with the specified id, specified using the `i` key.
| `c` or `C` | Delete all images that intersect with the current cursor position.
| `p` or `P` | Delete all images that intersect a specific cell, the cell is specified using the `x` and `y` keys
| `q` or `Q` | Delete all images that intersect a specific cell having a specific z-index. The cell and z-index is specified using the `x`, `y` and `z` keys.
| `x` or `X` | Delete all images that intersect the specified column, specified using the `x` key.
| `y` or `Y` | Delete all images that intersect the specified row, specified using the `y` key.
| `z` or `Z` | Delete all images that have the specified z-index, specified using the `z` key.
|===
Some examples:
```
<ESC>_Ga=d<ESC>\ # delete all visible images
<ESC>_Ga=d,i=10<ESC>\ # delete the image with id=10
<ESC>_Ga=Z,z=-1<ESC>\ # delete the images with z-index -1, also freeing up image data
<ESC>_Ga=P,x=3,y=4<ESC>\ # delete all images that intersect the cell at (3, 4)
```
=== Image persistence and storage quotas
In order to avoid *Denial-of-Service* attacks, terminal emulators should have a
maximum storage quota for image data. It should allow at least a few full
screen images. For example the quota in kitty is 320MB per buffer. When adding
a new image, if the total size exceeds the quota, the terminal emulator should
delete older images to make space for the new one.
== Control data reference
The table below shows all the control data keys as well as what values they can
take, and the default value they take when missing. All integers are 32-bit.
[cols="^1,<3,^1,<6"]
|===
|Key | Value | Default | Description
| `a` | Single character. `(t, T, q, p, d)` | `t` | The overall action this graphics command is performing.
4+^.^h| Keys for image transmission
| `f` | Positive integer. `(24, 32, 100)`. | `32` | The format in which the image data is sent.
| `t` | Single character. `(d, f, t, s)`. | `d` | The transmission medium used.
| `s` | Positive integer. | `0` | The width of the image being sent.
| `v` | Positive integer. | `0` | The height of the image being sent.
| `S` | Positive integer. | `0` | The size of data to read from a file.
| `O` | Positive integer. | `0` | The offset from which to read data from a file.
| `i` | Positive integer. `(0 - 4294967295)` | `0` | The image id
| `o` | Single character. `only z` | `null` | The type of data compression.
| `m` | zero or one | `0` | Whether there is more chunked data available.
4+^.^h| Keys for image display
| `x` | Positive integer | `0` | The left edge (in pixels) of the image area to display
| `y` | Positive integer | `0` | The top edge (in pixels) of the image area to display
| `w` | Positive integer | `0` | The width (in pixels) of the image area to display. By default, the entire width is used.
| `h` | Positive integer | `0` | The height (in pixels) of the image area to display. By default, the entire height is used
| `X` | Positive integer | `0` | The x-offset within the first cell at which to start displaying the image
| `Y` | Positive integer | `0` | The y-offset within the first cell at which to start displaying the image
| `c` | Positive integer | `0` | The number of columns to display the image over
| `r` | Positive integer | `0` | The number of rows to display the image over
| `z` | Integer | `0` | The *z-index* vertical stacking order of the image
4+^.^h| Keys for deleting images
| `d` | Single character. `(a, A, c, C, p, P, q, Q, x, X, y, Y, z, Z)`. | `a` | What to delete.
|===
== Interaction with other terminal actions
When resetting the terminal, all images that are visible on the screen must be
cleared. When switching from the main screen to the alternate screen buffer
(1049 private mode) all images in the alternate screen must be cleared, just as
all text is cleared. The clear screen escape code (usually `<ESC>[2J`) should also
clear all images. This is so that the clear command works.
The other commands to erase text must have no effect on graphics.
The dedicated delete graphics commands must be used for those.
When scrolling the screen (such as when using index cursor movement commands,
or scrolling through the history buffer), images must be scrolled along with
text. When page margins are defined and the index commands are used, only
images that are entirely within the page area (between the margins) must be
scrolled. When scrolling them would cause them to extend outside the page area,
they must be clipped.
See https://sw.kovidgoyal.net/kitty/graphics-protocol.html

View File

@@ -45,15 +45,19 @@ type=bool-set
Use the primary selection rather than the clipboard on systems that support it,
such as X11.
'''.format
help_text = '''\
Read or write to the system clipboard.
To set the clipboard text, pipe in the new text on stdin. Use the
:option:`--get-clipboard` option to output the current clipboard contents to
:file:`stdout`. Note that you must enable reading of clipboard in
:file:`kitty.conf` first.
'''
usage = ''
def main(args):
msg = '''\
Read or write to the system clipboard.
To set the clipboard text, pipe in the new text on stdin. Use the --get-clipboard option \
to output the current clipboard contents to stdout. Note that you must enable reading of clipboard in kitty.conf first. '''
args, items = parse_args(args[1:], OPTIONS, '', msg, 'clipboard')
args, items = parse_args(args[1:], OPTIONS, usage, help_text, 'kitty +kitten clipboard')
if items:
raise SystemExit('Unrecognized extra command line arguments')
data = None
@@ -74,3 +78,7 @@ to output the current clipboard contents to stdout. Note that you must enable re
if __name__ == '__main__':
main(sys.argv)
elif __name__ == '__doc__':
sys.cli_docs['usage'] = usage
sys.cli_docs['options'] = OPTIONS
sys.cli_docs['help_text'] = help_text

View File

@@ -1,127 +1 @@
= kitty-diff - A side-by-side diff tool with syntax highlighting and images
:toc:
:toc-placement!:
== Major Features
* Displays diffs side-by-side in the kitty terminal.
* Does syntax highlighting of displayed diffs
* Displays images as well as text diffs, even over SSH
* Does recursive directory diffing
image::../../screenshots/diff.png?raw=true[Screenshot, showing a sample diff]
toc::[]
== Installation
Simply install link:https://github.com/kovidgoyal/kitty[kitty]. You also need
to have either the link:https://git-scm.com/[git] program or the `diff` program
installed. Additionally, for syntax highlighting to work,
link:http://pygments.org/[pygments] must be installed (note that pygments is
included in the macOS kitty app).
== Usage
In the kitty terminal, run:
....
kitty +kitten diff file1 file2
....
to see the diff between file1 and file2.
Create an alias in your shell's startup file to shorten the command, for example:
```sh
alias d="kitty +kitten diff"
```
Now all you need to do to diff two files is:
```
d file1 file2
```
You can also pass directories instead of files to see the recursive diff of the
directory contents.
== Keyboard controls
|===
|Action |Shortcut
|Quit | `q, Ctrl+c`
|Scroll line up | `k, up`
|Scroll line down | `j, down`
|Scroll page up | `PgUp`
|Scroll page down | `PgDn`
|Scroll to top | `Home`
|Scroll to bottom | `End`
|Scroll to next change | `n`
|Scroll to previous change | `p`
|Increase lines of context | `+`
|Decrease lines of context | `-`
|All lines of context | `a`
|Restore context to default| `=`
|===
[options="header"]
== Configuring kitty-diff
You can configure the colors used, the diff implementation, the default lines of context, etc.
by creating a diff.conf in your kitty config folder. See the link:diff.conf[default diff.conf]
for details.
== Integrating with git
Add the following to `~/.gitconfig`:
```gitconfig
[diff]
tool = kitty
guitool = kitty.gui
[difftool]
prompt = false
trustExitCode = true
[difftool "kitty"]
cmd = kitty +kitten diff $LOCAL $REMOTE
[difftool "kitty.gui"]
cmd = kitty kitty +kitten diff $LOCAL $REMOTE
```
Now to use kitty-diff to view git diffs, you can simply do:
```
git difftool --no-symlinks --dir-diff
```
Once again, creating an alias for this command is useful.
== Why does this work only in kitty?
The diff kitten makes use of various features that are
link:https://github.com/kovidgoyal/kitty/blob/master/protocol-extensions.asciidoc[kitty
only], such as the
link:https://github.com/kovidgoyal/kitty/blob/master/graphics-protocol.asciidoc[kitty
graphics protocol], the extended keyboard protocol, etc. It also leverages
terminal program infrastructure I created for all of kitty's other kittens to
reduce the amount of code needed (the entire implementation is under 2000 lines
of code).
And fundamentally, it's kitty only because I wrote it for myself, and I am
highly unlikely to use any other terminals :)
See https://sw.kovidgoyal.net/kitty/kittens/diff.html

View File

@@ -116,12 +116,9 @@ def collect_files(collection, left, right):
sanitize_pat = re.compile('[\x00-\x09\x0b-\x1f\x7f\x80-\x9f]')
def sanitize_sub(m):
return '<{:x}>'.format(ord(m.group()[0]))
def sanitize(text):
return sanitize_pat.sub(sanitize_sub, text)
ntext = text.replace('\r\n', '\n')
return sanitize_pat.sub('', ntext)
@lru_cache(maxsize=1024)

View File

@@ -4,18 +4,17 @@
import os
from kitty.config_utils import (
init_config, key_func, load_config as _load_config, merge_dicts,
parse_config_base, parse_kittens_key, python_string, resolve_config,
to_color
from kitty.conf.utils import (
init_config as _init_config, key_func, load_config as _load_config, merge_dicts,
parse_config_base, parse_kittens_key, resolve_config
)
from kitty.conf.definition import config_lines
from kitty.constants import config_dir
from kitty.rgb import color_as_sgr
from .config_data import type_map, all_options
defaults = None
default_config_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)), 'diff.conf'
)
formats = {
'title': '',
@@ -39,26 +38,6 @@ def set_formats(opts):
formats['added_highlight'] = '48' + color_as_sgr(opts.highlight_added_bg)
def syntax_aliases(raw):
ans = {}
for x in raw.split():
a, b = x.partition(':')[::2]
if a and b:
ans[a.lower()] = b
return ans
type_map = {
'syntax_aliases': syntax_aliases,
'num_context_lines': int,
'replace_tab_by': python_string,
}
for name in (
'foreground background title_fg title_bg margin_bg margin_fg removed_bg removed_margin_bg added_bg added_margin_bg filler_bg hunk_bg hunk_margin_bg'
' highlight_removed_bg highlight_added_bg'
).split():
type_map[name] = to_color
func_with_args, args_funcs = key_func()
@@ -73,7 +52,7 @@ def parse_scroll_by(func, rest):
@func_with_args('scroll_to')
def parse_scroll_to(func, rest):
rest = rest.lower()
if rest not in {'start', 'end', 'next-change', 'prev-change', 'next-page', 'prev-page'}:
if rest not in {'start', 'end', 'next-change', 'prev-change', 'next-page', 'prev-page', 'next-match', 'prev-match'}:
rest = 'start'
return func, rest
@@ -90,6 +69,14 @@ def parse_change_context(func, rest):
return func, amount
@func_with_args('start_search')
def parse_start_search(func, rest):
rest = rest.lower().split()
is_regex = rest and rest[0] == 'regex'
is_backward = len(rest) > 1 and rest[1] == 'backward'
return func, (is_regex, is_backward)
def special_handling(key, val, ans):
if key == 'map':
action, *key_def = parse_kittens_key(val, args_funcs)
@@ -125,7 +112,7 @@ def parse_defaults(lines, check_keys=False):
return parse_config(lines, check_keys)
Options, defaults = init_config(default_config_path, parse_defaults)
Options, defaults = _init_config(config_lines(all_options), parse_defaults)
def load_config(*paths, overrides=None):

124
kittens/diff/config_data.py Normal file
View File

@@ -0,0 +1,124 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
# Utils {{{
from gettext import gettext as _
from functools import partial
from kitty.conf.definition import option_func
from kitty.conf.utils import (
positive_int, python_string, to_color
)
# }}}
all_options = {}
o, k, g, all_groups = option_func(all_options, {
'colors': [_('Colors')],
'diff': [_('Diffing'), ],
'shortcuts': [_('Keyboard shortcuts')],
})
g('diff')
def syntax_aliases(raw):
ans = {}
for x in raw.split():
a, b = x.partition(':')[::2]
if a and b:
ans[a.lower()] = b
return ans
o('syntax_aliases', 'pyj:py recipe:py', option_type=syntax_aliases, long_text=_('''
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`
'''))
o('num_context_lines', 3, option_type=positive_int, long_text=_('''
The number of lines of context to show around each change.'''))
o('diff_cmd', 'auto', long_text=_('''
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 system for either git or diff and use that, if found.
'''))
o('replace_tab_by', r'\x20\x20\x20\x20', option_type=python_string, long_text=_('''
The string to replace tabs with. Default is to use four spaces.'''))
g('colors')
o('pygments_style', 'default', long_text=_('''
The pygments color scheme to use for syntax highlighting.
See :link:`pygments colors schemes <https://help.farbox.com/pygments.html>` for a list of schemes.'''))
c = partial(o, option_type=to_color)
c('foreground', 'black', long_text=_('Basic colors'))
c('background', 'white')
c('title_fg', 'black', long_text=_('Title colors'))
c('title_bg', 'white')
c('margin_bg', '#fafbfc', long_text=_('Margin colors'))
c('margin_fg', '#aaaaaa')
c('removed_bg', '#ffeef0', long_text=_('Removed text backgrounds'))
c('highlight_removed_bg', '#fdb8c0')
c('removed_margin_bg', '#ffdce0')
c('added_bg', '#e6ffed', long_text=_('Added text backgrounds'))
c('highlight_added_bg', '#acf2bd')
c('added_margin_bg', '#cdffd8')
c('filler_bg', '#fafbfc', long_text=_('Filler (empty) line background'))
c('hunk_margin_bg', '#dbedff', long_text=_('Hunk header colors'))
c('hunk_bg', '#f1f8ff')
c('search_bg', '#444', long_text=_('Highlighting'))
c('search_fg', 'white')
c('select_bg', '#B4D5FE')
c('select_fg', 'black')
g('shortcuts')
k('quit', 'q', 'quit', _('Quit'))
k('quit', 'esc', 'quit', _('Quit'))
k('scroll_down', 'j', 'scroll_by 1', _('Scroll down'))
k('scroll_down', 'down', 'scroll_by 1', _('Scroll down'))
k('scroll_up', 'k', 'scroll_by -1', _('Scroll up'))
k('scroll_up', 'up', 'scroll_by -1', _('Scroll up'))
k('scroll_top', 'home', 'scroll_to start', _('Scroll to top'))
k('scroll_bottom', 'end', 'scroll_to end', _('Scroll to bottom'))
k('scroll_page_down', 'page_down', 'scroll_to next-page', _('Scroll to next page'))
k('scroll_page_down', 'space', 'scroll_to next-page', _('Scroll to next page'))
k('scroll_page_up', 'page_up', 'scroll_to prev-page', _('Scroll to previous page'))
k('next_change', 'n', 'scroll_to next-change', _('Scroll to next change'))
k('prev_change', 'p', 'scroll_to prev-change', _('Scroll to previous change'))
k('all_context', 'a', 'change_context all', _('Show all context'))
k('default_context', '=', 'change_context default', _('Show default context'))
k('increase_context', '+', 'change_context 5', _('Increase context'))
k('decrease_context', '-', 'change_context -5', _('Decrease context'))
k('search_forward', '/', 'start_search regex forward', _('Search forward'))
k('search_backward', '?', 'start_search regex backward', _('Search backward'))
k('next_match', '.', 'scroll_to next-match', _('Scroll to next search match'))
k('prev_match', ',', 'scroll_to prev-match', _('Scroll to previous search match'))
k('next_match', '>', 'scroll_to next-match', _('Scroll to next search match'))
k('prev_match', '<', 'scroll_to prev-match', _('Scroll to previous search match'))
k('search_forward_simple', 'f', 'start_search substring forward', _('Search forward (no regex)'))
k('search_backward_simple', 'b', 'start_search substring backward', _('Search backward (no regex)'))
type_map = {o.name: o.option_type for o in all_options.values() if hasattr(o, 'option_type')}

View File

@@ -1,58 +0,0 @@
# vim:fileencoding=utf-8:ft=conf
# File extension aliases for syntax highlight
# For example, to syntax highlight file.xyz as
# file.abc use a setting of xyz:abc
syntax_aliases pyj:py recipe:py
# The pygments color scheme to use for syntax highlighting.
# See https://help.farbox.com/pygments.html for a list of schemes.
pygments_style default
# The number of lines of context to show around each change.
num_context_lines 3
# The diff command to use. Must contain the placeholder _CONTEXT_
# which will be replaced by the number of lines of context. The default
# is to search the system for either git or diff and use that, if found.
diff_cmd auto
# The string to replace tabs with. Default is to use four spaces.
replace_tab_by \x20\x20\x20\x20
# Colors
foreground black
background white
title_fg black
title_bg white
margin_bg #fafbfc
margin_fg #aaaaaa
removed_bg #ffeef0
highlight_removed_bg #fdb8c0
removed_margin_bg #ffdce0
added_bg #e6ffed
highlight_added_bg #acf2bd
added_margin_bg #cdffd8
filler_bg #fafbfc
hunk_margin_bg #dbedff
hunk_bg #f1f8ff
# Keyboard shortcuts
map q quit
map esc quit
map j scroll_by 1
map k scroll_by -1
map down scroll_by 1
map up scroll_by -1
map home scroll_to start
map end scroll_to end
map page_down scroll_to next-page
map page_up scroll_to prev-page
map space scroll_to next-page
map n scroll_to next-change
map p scroll_to prev-change
map a change_context all
map = change_context default
map + change_context 5
map - change_context -5

View File

@@ -9,20 +9,24 @@ from collections import defaultdict
from functools import partial
from gettext import gettext as _
from kitty.cli import CONFIG_HELP, appname, parse_args
from kitty.cli import CONFIG_HELP, parse_args
from kitty.constants import appname
from kitty.fast_data_types import wcswidth
from kitty.key_encoding import RELEASE
from kitty.key_encoding import ESCAPE, RELEASE, enter_key
from ..tui.handler import Handler
from ..tui.images import ImageManager
from ..tui.line_edit import LineEdit
from ..tui.loop import Loop
from ..tui.operations import styled
from .collect import (
create_collection, data_for_path, lines_for_path, set_highlight_data
create_collection, data_for_path, lines_for_path, sanitize,
set_highlight_data
)
from .config import init_config
from .patch import Differ, set_diff_command
from .render import ImageSupportWarning, LineRef, render_diff
from .search import BadRegex, Search
try:
from .highlight import initialize_highlighter, highlight_collection
@@ -30,7 +34,7 @@ except ImportError:
initialize_highlighter = None
INITIALIZING, COLLECTED, DIFFED = range(3)
INITIALIZING, COLLECTED, DIFFED, COMMAND, MESSAGE = range(5)
def generate_diff(collection, context):
@@ -51,6 +55,10 @@ class DiffHandler(Handler):
def __init__(self, args, opts, left, right):
self.state = INITIALIZING
self.message = ''
self.current_search_is_regex = True
self.current_search = None
self.line_edit = LineEdit()
self.opts = opts
self.left, self.right = left, right
self.report_traceback_on_exit = None
@@ -76,6 +84,8 @@ class DiffHandler(Handler):
where = args[0]
if 'change' in where:
return self.scroll_to_next_change(backwards='prev' in where)
if 'match' in where:
return self.scroll_to_next_match(backwards='prev' in where)
if 'page' in where:
amt = self.num_lines * (1 if 'next' in where else -1)
else:
@@ -91,6 +101,9 @@ class DiffHandler(Handler):
else:
new_ctx += to
return self.change_context_count(new_ctx)
if func == 'start_search':
self.start_search(*args)
return
def create_collection(self):
self.start_job('collect', create_collection, self.left, self.right)
@@ -107,10 +120,13 @@ class DiffHandler(Handler):
def render_diff(self):
self.diff_lines = tuple(render_diff(self.collection, self.diff_map, self.args, self.screen_size.cols, self.image_manager))
self.margin_size = render_diff.margin_size
self.ref_path_map = defaultdict(list)
for i, l in enumerate(self.diff_lines):
self.ref_path_map[l.ref.path].append((i, l.ref))
self.max_scroll_pos = len(self.diff_lines) - self.num_lines
if self.current_search is not None:
self.current_search(self.diff_lines, self.margin_size, self.screen_size.cols)
@property
def current_position(self):
@@ -152,6 +168,18 @@ class DiffHandler(Handler):
return
self.cmd.bell()
def scroll_to_next_match(self, backwards=False):
if self.current_search is not None:
if backwards:
r = range(self.scroll_pos - 1, -1, -1)
else:
r = range(self.scroll_pos + 1, len(self.diff_lines))
for i in r:
if i in self.current_search:
self.scroll_lines(i - self.scroll_pos)
return
self.cmd.bell()
def set_scrolling_region(self):
self.cmd.set_scrolling_region(self.screen_size, 0, self.num_lines - 2)
@@ -178,7 +206,16 @@ class DiffHandler(Handler):
def init_terminal_state(self):
self.cmd.set_line_wrapping(False)
self.cmd.set_window_title(main.title)
self.cmd.set_default_colors(self.opts.foreground, self.opts.background)
self.cmd.set_default_colors(
fg=self.opts.foreground, bg=self.opts.background,
cursor=self.opts.foreground, select_fg=self.opts.select_fg,
select_bg=self.opts.select_bg)
self.cmd.set_cursor_shape('bar')
def finalize(self):
self.cmd.set_default_colors()
self.cmd.set_cursor_visible(True)
self.cmd.set_scrolling_region()
def initialize(self):
self.init_terminal_state()
@@ -187,12 +224,7 @@ class DiffHandler(Handler):
self.create_collection()
def enforce_cursor_state(self):
self.cmd.set_cursor_visible(self.state > DIFFED)
def finalize(self):
self.cmd.set_cursor_visible(True)
self.cmd.set_default_colors()
self.cmd.set_scrolling_region()
self.cmd.set_cursor_visible(self.state == COMMAND)
def draw_lines(self, num, offset=0):
offset += self.scroll_pos
@@ -208,6 +240,8 @@ class DiffHandler(Handler):
if line.image_data is not None:
image_involved = True
self.write('\r\x1b[K' + text + '\x1b[0m')
if self.current_search is not None:
self.current_search.highlight_line(self.write, lpos)
if i < num - 1:
self.write('\n')
if image_involved:
@@ -262,19 +296,29 @@ class DiffHandler(Handler):
def draw_status_line(self):
if self.state < DIFFED:
return
self.enforce_cursor_state()
self.cmd.set_cursor_position(0, self.num_lines)
self.cmd.clear_to_eol()
scroll_frac = styled('{:.0%}'.format(self.scroll_pos / (self.max_scroll_pos or 1)), fg=self.opts.margin_fg)
counts = '{}{}{}'.format(
styled(str(self.added_count), fg=self.opts.highlight_added_bg),
styled(',', fg=self.opts.margin_fg),
styled(str(self.removed_count), fg=self.opts.highlight_removed_bg)
)
suffix = counts + ' ' + scroll_frac
prefix = styled(':', fg=self.opts.margin_fg)
filler = self.screen_size.cols - wcswidth(prefix) - wcswidth(suffix)
text = '{}{}{}'.format(prefix, ' ' * filler, suffix)
self.write(text)
if self.state is COMMAND:
self.line_edit.write(self.write)
elif self.state is MESSAGE:
self.cmd.styled(self.message, reverse=True)
else:
sp = '{:.0%}'.format(self.scroll_pos/self.max_scroll_pos) if self.scroll_pos and self.max_scroll_pos else '0%'
scroll_frac = styled(sp, fg=self.opts.margin_fg)
if self.current_search is None:
counts = '{}{}{}'.format(
styled(str(self.added_count), fg=self.opts.highlight_added_bg),
styled(',', fg=self.opts.margin_fg),
styled(str(self.removed_count), fg=self.opts.highlight_removed_bg)
)
else:
counts = styled('{} matches'.format(len(self.current_search)), fg=self.opts.margin_fg)
suffix = counts + ' ' + scroll_frac
prefix = styled(':', fg=self.opts.margin_fg)
filler = self.screen_size.cols - wcswidth(prefix) - wcswidth(suffix)
text = '{}{}{}'.format(prefix, ' ' * filler, suffix)
self.write(text)
def change_context_count(self, new_ctx):
new_ctx = max(0, new_ctx)
@@ -285,14 +329,75 @@ class DiffHandler(Handler):
self.restore_position = self.current_position
self.draw_screen()
def start_search(self, is_regex, is_backward):
if self.state != DIFFED:
self.cmd.bell()
return
self.state = COMMAND
self.line_edit.clear()
self.line_edit.add_text('?' if is_backward else '/')
self.current_search_is_regex = is_regex
self.draw_status_line()
def do_search(self):
self.current_search = None
query = self.line_edit.current_input
if len(query) < 2:
return
try:
self.current_search = Search(self.opts, query[1:], self.current_search_is_regex, query[0] == '?')
except BadRegex:
self.state = MESSAGE
self.message = sanitize(_('Bad regex: {}').format(query[1:]))
self.cmd.bell()
else:
if not self.current_search(self.diff_lines, self.margin_size, self.screen_size.cols):
self.state = MESSAGE
self.message = sanitize(_('No matches found'))
self.cmd.bell()
def on_text(self, text, in_bracketed_paste=False):
if self.state is COMMAND:
self.line_edit.on_text(text, in_bracketed_paste)
self.draw_status_line()
return
if self.state is MESSAGE:
self.state = DIFFED
self.draw_status_line()
return
action = self.shortcut_action(text)
if action is not None:
return self.perform_action(action)
def on_key(self, key_event):
if self.state is MESSAGE:
if key_event.type is not RELEASE:
self.state = DIFFED
self.draw_status_line()
return
if self.state is COMMAND:
if self.line_edit.on_key(key_event):
if not self.line_edit.current_input:
self.state = DIFFED
self.draw_status_line()
return
if key_event.type is RELEASE:
return
if self.state is COMMAND:
if key_event.key is ESCAPE:
self.state = DIFFED
self.draw_status_line()
return
if key_event is enter_key:
self.state = DIFFED
self.do_search()
self.line_edit.clear()
self.draw_screen()
return
if self.state >= DIFFED and self.current_search is not None and key_event.key is ESCAPE:
self.current_search = None
self.draw_screen()
return
action = self.shortcut_action(key_event)
if action is not None:
return self.perform_action(action)
@@ -372,7 +477,7 @@ type=list
--override -o
type=list
Override individual configuration options, can be specified multiple times.
Syntax: |_ name=value|. For example: |_ -o background=gray|
Syntax: :italic:`name=value`. For example: :italic:`-o background=gray`
'''.format, config_help=CONFIG_HELP.format(conf_name='diff', appname=appname))
@@ -383,12 +488,13 @@ def showwarning(message, category, filename, lineno, file=None, line=None):
showwarning.warnings = []
help_text = 'Show a side-by-side diff of the specified files/directories'
usage = 'file_or_directory_left file_or_directory_right'
def main(args):
warnings.showwarning = showwarning
msg = 'Show a side-by-side diff of the specified files/directories'
args, items = parse_args(args[1:], OPTIONS, 'file_or_directory file_or_directory', msg, 'kitty +kitten diff')
args, items = parse_args(args[1:], OPTIONS, usage, help_text, 'kitty +kitten diff')
if len(items) != 2:
raise SystemExit('You must specify exactly two files/directories to compare')
left, right = items
@@ -398,6 +504,9 @@ def main(args):
opts = init_config(args)
set_diff_command(opts.diff_cmd)
lines_for_path.replace_tab_by = opts.replace_tab_by
for f in left, right:
if not os.path.exists(f):
raise SystemExit('{} does not exist'.format(f))
loop = Loop()
handler = DiffHandler(args, opts, left, right)
@@ -414,3 +523,10 @@ def main(args):
if __name__ == '__main__':
main(sys.argv)
elif __name__ == '__doc__':
sys.cli_docs['usage'] = usage
sys.cli_docs['options'] = OPTIONS
sys.cli_docs['help_text'] = help_text
elif __name__ == '__conf__':
from .config import all_options
sys.all_options = all_options

View File

@@ -9,7 +9,7 @@ from math import ceil
from kitty.fast_data_types import truncate_point_for_length, wcswidth
from ..tui.images import can_display_images, screen_size
from ..tui.images import can_display_images
from .collect import (
Segment, data_for_path, highlights_for_path, is_image, lines_for_path,
path_name_map, sanitize
@@ -371,12 +371,11 @@ def rename_lines(path, other_path, args, columns, margin_size):
class Image:
def __init__(self, image_id, width, height, margin_size):
def __init__(self, image_id, width, height, margin_size, screen_size):
self.image_id = image_id
self.width, self.height = width, height
ss = screen_size()
self.rows = int(ceil(self.height / ss.cell_height))
self.columns = int(ceil(self.width / ss.cell_width))
self.rows = int(ceil(self.height / screen_size.cell_height))
self.columns = int(ceil(self.width / screen_size.cell_width))
self.margin_size = margin_size
@@ -400,7 +399,7 @@ def render_image(path, is_left, available_cols, margin_size, image_manager):
lnum += 1
try:
image_id, width, height = image_manager.send_image(path, available_cols - margin_size, screen_size().rows - 2)
image_id, width, height = image_manager.send_image(path, available_cols - margin_size, image_manager.screen_size.rows - 2)
except Exception as e:
yield from yield_split(_('Failed to render image, with error:'))
yield from yield_split(' '.join(str(e).splitlines()))
@@ -409,7 +408,7 @@ def render_image(path, is_left, available_cols, margin_size, image_manager):
width, height, human_readable(len(data_for_path(path))))
yield from yield_split(meta)
bg_line = m + fmt(' ' * available_cols)
img = Image(image_id, width, height, margin_size)
img = Image(image_id, width, height, margin_size, image_manager.screen_size)
for r in range(img.rows):
yield bg_line, Reference(path, LineRef(lnum)), ImagePlacement(img, r)
lnum += 1
@@ -448,7 +447,7 @@ def render_diff(collection, diff_map, args, columns, image_manager):
if patch is not None:
largest_line_number = max(largest_line_number, patch.largest_line_number)
margin_size = max(3, len(str(largest_line_number)) + 1)
margin_size = render_diff.margin_size = max(3, len(str(largest_line_number)) + 1)
last_item_num = len(collection) - 1
for i, (path, item_type, other_path) in enumerate(collection):

67
kittens/diff/search.py Normal file
View File

@@ -0,0 +1,67 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import re
from kitty.fast_data_types import wcswidth
from ..tui.operations import styled
class BadRegex(ValueError):
pass
class Search:
def __init__(self, opts, query, is_regex, is_backward):
self.matches = {}
self.count = 0
self.style = styled('|', fg=opts.search_fg, bg=opts.search_bg).split('|', 1)[0]
if not is_regex:
query = re.escape(query)
try:
self.pat = re.compile(query, flags=re.UNICODE | re.IGNORECASE)
except Exception:
raise BadRegex('Not a valid regex: {}'.format(query))
def __call__(self, diff_lines, margin_size, cols):
self.matches = {}
self.count = 0
half_width = cols // 2
strip_pat = re.compile('\033[[].*?m')
right_offset = half_width + 1 + margin_size
find = self.pat.finditer
for i, line in enumerate(diff_lines):
text = strip_pat.sub('', line.text)
left, right = text[margin_size:half_width + 1], text[right_offset:]
matches = []
def add(which, offset):
for m in find(which):
before = which[:m.start()]
matches.append((wcswidth(before) + offset, m.group()))
self.count += 1
add(left, margin_size)
add(right, right_offset)
if matches:
self.matches[i] = matches
return bool(self.matches)
def __contains__(self, i):
return i in self.matches
def __len__(self):
return self.count
def highlight_line(self, write, line_num):
highlights = self.matches.get(line_num)
if not highlights:
return False
write(self.style)
for start, text in highlights:
write('\r\x1b[{}C{}'.format(start, text))
write('\x1b[m')
return True

View File

@@ -6,8 +6,9 @@ import os
import re
import string
import sys
from functools import lru_cache, partial
from functools import lru_cache
from gettext import gettext as _
from itertools import repeat
from kitty.cli import parse_args
from kitty.fast_data_types import set_clipboard_string
@@ -243,6 +244,8 @@ def functions_for(args):
post_processors.extend((brackets, quotes))
elif args.type == 'line':
pattern = '(?m)^\\s*(.+)[\\s\0]*$'
elif args.type == 'hash':
pattern = '[0-9a-f]{7,128}'
elif args.type == 'word':
chars = args.word_characters
if chars is None:
@@ -257,8 +260,12 @@ def functions_for(args):
def convert_text(text, cols):
lines = []
empty_line = '\0' * cols
for full_line in text.split('\n'):
if full_line:
if not full_line.rstrip('\r'): # empty lines
lines.extend(repeat(empty_line, len(full_line)))
continue
for line in full_line.split('\r'):
if line:
lines.append(line.ljust(cols, '\0'))
@@ -298,25 +305,26 @@ def run(args, text):
# CLI {{{
OPTIONS = partial(r'''
OPTIONS = r'''
--program
default=default
What program to use to open matched text. Defaults to the default open program
for the operating system. Use a value of - to paste the match into the
terminal window instead. A value of @ will copy the match to the clipboard.
for the operating system. Use a value of :file:`-` to paste the match into the
terminal window instead. A value of :file:`@` will copy the match to the clipboard.
--type
default=url
choices=url,regex,path,line,word
choices=url,regex,path,line,hash,word
The type of text to search for.
--regex
default=(?m)^\s*(.+)\s*$
The regular expression to use when --type=regex. If you specify a group in the
regular expression only the group will be matched. This allow you to match text
ignoring a prefix/suffix, as needed. The default expression matches lines.
The regular expression to use when :option:`kitty +kitten hints --type`=regex.
If you specify a group in the regular expression only the group
will be matched. This allow you to match text ignoring a prefix/suffix, as
needed. The default expression matches lines.
--url-prefixes
@@ -334,12 +342,13 @@ Defaults to the select_by_word_characters setting from kitty.conf.
default=3
type=int
The minimum number of characters to consider a match.
'''.format, ','.join(sorted(URL_PREFIXES)))
'''.format(','.join(sorted(URL_PREFIXES))).format
help_text = 'Select text from the screen using the keyboard. Defaults to searching for URLs.'
usage = ''
def parse_hints_args(args):
msg = 'Select text from the screen using the keyboard. Defaults to searching for URLs.'
return parse_args(args, OPTIONS, '', msg, 'hints')
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten hints')
def main(args):
@@ -390,4 +399,8 @@ if __name__ == '__main__':
ans = main(sys.argv)
if ans:
print(ans)
elif __name__ == '__doc__':
sys.cli_docs['usage'] = usage
sys.cli_docs['options'] = OPTIONS
sys.cli_docs['help_text'] = help_text
# }}}

View File

@@ -1 +1 @@
url_delimiters = '\x00-\x09\x0b-\x20\x7f-\xa0\xad\u0600-\u0605\u061c\u06dd\u070f\u08e2\u1680\u180e\u2000-\u200f\u2028-\u202f\u205f-\u2064\u2066-\u206f\u3000\ud800-\uf8ff\ufeff\ufff9-\ufffb\U000110bd\U0001bca0-\U0001bca3\U0001d173-\U0001d17a\U000e0001\U000e0020-\U000e007f\U000f0000-\U000ffffd\U00100000-\U0010fffd' # noqa
url_delimiters = '\x00-\x09\x0b-\x20\x7f-\xa0\xad\u0600-\u0605\u061c\u06dd\u070f\u08e2\u1680\u180e\u2000-\u200f\u2028-\u202f\u205f-\u2064\u2066-\u206f\u3000\ud800-\uf8ff\ufeff\ufff9-\ufffb\U000110bd\U000110cd\U0001bca0-\U0001bca3\U0001d173-\U0001d17a\U000e0001\U000e0020-\U000e007f\U000f0000-\U000ffffd\U00100000-\U0010fffd' # noqa

View File

@@ -15,14 +15,14 @@ from tempfile import NamedTemporaryFile
from kitty.cli import parse_args
from kitty.constants import appname
from kitty.utils import fit_image, read_with_timeout
from kitty.utils import TTYIO, fit_image, screen_size_function
from ..tui.images import (
ConvertFailed, NoImageMagick, OpenFailed, convert, fsenc, identify,
screen_size
ConvertFailed, NoImageMagick, OpenFailed, convert, fsenc, identify
)
from ..tui.operations import clear_images_on_screen, serialize_gr_command
screen_size = None
OPTIONS = '''\
--align
type=choices
@@ -34,14 +34,14 @@ Horizontal alignment for the displayed image.
--place
Choose where on the screen to display the image. The image will
be scaled to fit into the specified rectangle. The syntax for
specifying rectanges is <|_ width|>x<|_ height|>@<|_ left|>x<|_ top|>. All measurements
are in cells (i.e. cursor positions) with the origin |_ (0, 0)| at
specifying rectanges is <:italic:`width`>x<:italic:`height`>@<:italic:`left`>x<:italic:`top`>. All measurements
are in cells (i.e. cursor positions) with the origin :italic:`(0, 0)` at
the top-left corner of the screen.
--scale-up
type=bool-set
When used in combination with |_ --place| it will cause images that
When used in combination with :option:`--place` it will cause images that
are smaller than the specified area to be scaled up to use as much
of the specified area as possible.
@@ -56,8 +56,8 @@ type=choices
choices=detect,file,stream
default=detect
Which mechanism to use to transfer images to the terminal. The default is to
auto-detect. |_ file| means to use a temporary file and |_ stream| means to
send the data via terminal escape codes. Note that if you use the |_ file|
auto-detect. :italic:`file` means to use a temporary file and :italic:`stream` means to
send the data via terminal escape codes. Note that if you use the :italic:`file`
transfer mode and you are connecting over a remote session then image display
will not work.
@@ -66,7 +66,7 @@ will not work.
type=bool-set
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
transfer mode to stderr, which can be used with the |_ --transfer-mode| option.
transfer mode to stderr, which can be used with the :option:`--transfer-mode` option.
--detection-timeout
@@ -74,6 +74,13 @@ type=float
default=10
The amount of time (in seconds) to wait for a response form the terminal, when
detecting image display support.
--print-window-size
type=bool-set
Print out the window size as :italic:`widthxheight` (in pixels) and quit. This is a
convenience method to query the window size if using kitty icat from a
scripting language that cannot make termios calls.
'''
@@ -222,7 +229,8 @@ def detect_support(wait_for=10, silent=False):
f.write(b'abcd'), f.flush()
write_gr_cmd(dict(a='q', s=1, v=1, i=1), standard_b64encode(b'abcd'))
write_gr_cmd(dict(a='q', s=1, v=1, i=2, t='f'), standard_b64encode(f.name.encode(fsenc)))
read_with_timeout(more_needed, timeout=wait_for)
with TTYIO() as io:
io.recv(more_needed, timeout=float(wait_for))
finally:
if not silent:
sys.stdout.buffer.write(b'\033[J'), sys.stdout.flush()
@@ -238,18 +246,29 @@ def parse_place(raw):
return namedtuple('Place', 'width height left top')(w, h, l, t)
def main(args=sys.argv):
msg = (
help_text = (
'A cat like utility to display images in the terminal.'
' You can specify multiple image files and/or directories.'
' Directories are scanned recursively for image files.')
args, items = parse_args(args[1:], options_spec, 'image-file ...', msg, '{} icat'.format(appname))
' Directories are scanned recursively for image files.'
)
usage = 'image-file ...'
def main(args=sys.argv):
global screen_size
args, items = parse_args(args[1:], options_spec, usage, help_text, '{} +kitten icat'.format(appname))
if args.print_window_size:
screen_size_function.ans = None
with open('/dev/tty') as tty:
ss = screen_size_function(tty)()
print('{}x{}'.format(ss.width, ss.height), end='')
raise SystemExit(0)
if not sys.stdout.isatty():
sys.stdout = open('/dev/tty', 'w')
screen_size = screen_size_function()
signal.signal(signal.SIGWINCH, lambda signum, frame: setattr(screen_size, 'changed', True))
if not sys.stdout.isatty() or not sys.stdin.isatty():
raise SystemExit(
'Must be run in a terminal, stdout and/or stdin is currently not a terminal'
)
if screen_size().width == 0:
if args.detect_support:
raise SystemExit(1)
@@ -306,3 +325,7 @@ def main(args=sys.argv):
if __name__ == '__main__':
main()
elif __name__ == '__doc__':
sys.cli_docs['usage'] = usage
sys.cli_docs['options'] = options_spec
sys.cli_docs['help_text'] = help_text

View File

148
kittens/panel/main.py Normal file
View File

@@ -0,0 +1,148 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import subprocess
import sys
from kitty.constants import is_macos, is_wayland
from kitty.cli import parse_args
OPTIONS = r'''
--lines
type=int
default=1
The number of lines shown in the panel (the height of the panel). Applies to horizontal panels.
--columns
type=int
default=20
The number of columns shown in the panel (the width of the panel). Applies to vertical panels.
--edge
choices=top,bottom,left,right
default=top
Which edge of the screen to place the panel on. Note that some window managers
(such as i3) do not support placing docked windows on the left and right edges.
--config -c
type=list
Path to config file to use for kitty when drawing the panel.
--override -o
type=list
Override individual kitty configuration options, can be specified multiple times.
Syntax: :italic:`name=value`. For example: :option:`kitty +kitten panel -o` font_size=20
'''.format
args = None
help_text = 'Use a command line program to draw a GPU accelerated panel on your X11 desktop'
usage = 'program-to-run'
def parse_panel_args(args):
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten panel')
def call_xprop(*cmd, silent=False):
cmd = ['xprop'] + list(cmd)
try:
cp = subprocess.run(cmd, stdout=subprocess.DEVNULL if silent else None)
except FileNotFoundError:
raise SystemExit('You must have the xprop program installed')
if cp.returncode != 0:
raise SystemExit(cp.returncode)
def create_strut(
win_id,
left=0, right=0, top=0, bottom=0, left_start_y=0, left_end_y=0,
right_start_y=0, right_end_y=0, top_start_x=0, top_end_x=0,
bottom_start_x=0, bottom_end_x=0
):
call_xprop(
'-id',
str(int(win_id)), '-format', '_NET_WM_STRUT_PARTIAL', '32cccccccccccc',
'-set', '_NET_WM_STRUT_PARTIAL',
'{left},{right},{top},{bottom},'
'{left_start_y},{left_end_y},{right_start_y},{right_end_y},'
'{top_start_x},{top_end_x},{bottom_start_x},{bottom_end_x}'.format(**locals())
)
def create_top_strut(win_id, width, height):
create_strut(win_id, top=height, top_end_x=width)
def create_bottom_strut(win_id, width, height):
create_strut(win_id, bottom=height, bottom_end_x=width)
def create_left_strut(win_id, width, height):
create_strut(win_id, left=width, left_end_y=height)
def create_right_strut(win_id, width, height):
create_strut(win_id, right=width, right_end_y=height)
def setup_x11_window(win_id):
call_xprop(
'-id', str(win_id), '-format', '_NET_WM_WINDOW_TYPE', '32a',
'-set', '_NET_WM_WINDOW_TYPE', '_NET_WM_WINDOW_TYPE_DOCK'
)
func = globals()['create_{}_strut'.format(args.edge)]
func(win_id, initial_window_size_func.width, initial_window_size_func.height)
def initial_window_size_func(opts, *a):
from kitty.fast_data_types import glfw_primary_monitor_size, set_smallest_allowed_resize
def initial_window_size(cell_width, cell_height, dpi_x, dpi_y):
monitor_width, monitor_height = glfw_primary_monitor_size()
if args.edge in {'top', 'bottom'}:
h = initial_window_size_func.height = cell_height * args.lines + 1
initial_window_size_func.width = monitor_width
set_smallest_allowed_resize(100, h)
else:
w = initial_window_size_func.width = cell_width * args.columns + 1
initial_window_size_func.height = monitor_height
set_smallest_allowed_resize(w, 100)
return initial_window_size_func.width, initial_window_size_func.height
return initial_window_size
def main(sys_args):
global args
if is_macos or is_wayland:
raise SystemExit('Currently the panel kitten is supported only on X11 desktops')
call_xprop('-version', silent=True) # ensure xprop is available
args, items = parse_panel_args(sys_args[1:])
if not items:
raise SystemExit('You must specify the program to run')
sys.argv = ['kitty']
if args.config:
sys.argv.append('--config={}'.format(args.config))
for override in args.override:
sys.argv.append('--override={}'.format(override))
sys.argv.extend(items)
from kitty.main import run_app, main
run_app.cached_values_name = 'panel'
run_app.first_window_callback = setup_x11_window
run_app.initial_window_size_func = initial_window_size_func
main()
if __name__ == '__main__':
main(sys.argv)
elif __name__ == '__doc__':
sys.cli_docs['usage'] = usage
sys.cli_docs['options'] = OPTIONS
sys.cli_docs['help_text'] = help_text

View File

@@ -12,7 +12,7 @@ aliases = {'url_hints': 'hints'}
def resolved_kitten(k):
return aliases.get(k, k)
return aliases.get(k, k).replace('-', '_')
def import_kitten_main_module(config_dir, kitten):
@@ -80,11 +80,53 @@ def deserialize(output):
return json.loads(rest[:int(sz)])
def run_kitten(kitten):
def run_kitten(kitten, run_name='__main__'):
import runpy
kitten = resolved_kitten(kitten)
set_debug(kitten)
runpy.run_module('kittens.{}.main'.format(kitten), run_name='__main__')
try:
runpy.run_module('kittens.{}.main'.format(kitten), run_name=run_name)
except ImportError:
raise SystemExit('No kitten named {}'.format(kitten))
def all_kitten_names():
ans = getattr(all_kitten_names, 'ans', None)
if ans is None:
n = []
import glob
base = os.path.dirname(os.path.abspath(__file__))
for x in glob.glob(os.path.join(base, '*', '__init__.py')):
q = os.path.basename(os.path.dirname(x))
if q != 'tui':
n.append(q)
all_kitten_names.ans = ans = frozenset(n)
return ans
def list_kittens():
print('You must specify the name of a kitten to run')
print('Choose from:')
print()
for kitten in all_kitten_names():
print(kitten)
def get_kitten_cli_docs(kitten):
sys.cli_docs = {}
run_kitten(kitten, run_name='__doc__')
ans = sys.cli_docs
del sys.cli_docs
if 'help_text' in ans and 'usage' in ans and 'options' in ans:
return ans
def get_kitten_conf_docs(kitten):
sys.all_options = None
run_kitten(kitten, run_name='__conf__')
ans = sys.all_options
del sys.all_options
return ans
def main():

View File

@@ -2,6 +2,8 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import os
import re
import subprocess
import sys
@@ -19,9 +21,9 @@ rc=$?
rm $tmp
if [ "$rc" != "0" ]; then echo "$tic_out"; exit 1; fi
if [ -z "$USER" ]; then export USER=$(whoami); fi
EXEC_CMD
shell_name=$(basename $0)
# 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
@@ -34,17 +36,78 @@ case "dash" in
exec $python -c "import os; os.execlp('$0', '-' '$shell_name')"
;;
esac
exec -a "-$shell_name" "$0"
'''
def get_ssh_cli():
other_ssh_args, boolean_ssh_args = [], []
raw = subprocess.Popen(['ssh'], stderr=subprocess.PIPE).stderr.read().decode('utf-8')
for m in re.finditer(r'\[(.+?)\]', raw):
q = m.group(1)
if len(q) < 2 or q[0] != '-':
continue
if ' ' in q:
other_ssh_args.append(q[1])
else:
boolean_ssh_args.extend(q[1:])
return set('-' + x for x in boolean_ssh_args), set('-' + x for x in other_ssh_args)
def parse_ssh_args(args):
boolean_ssh_args, other_ssh_args = get_ssh_cli()
passthrough_args = {'-' + x for x in 'Nnf'}
ssh_args = []
server_args = []
expecting_option_val = False
passthrough = False
for arg in args:
if len(server_args) > 1:
server_args.append(arg)
continue
if arg.startswith('-') and not expecting_option_val:
all_args = arg[1:]
for i, arg in enumerate(all_args):
arg = '-' + arg
if arg in passthrough_args:
passthrough = True
if arg in boolean_ssh_args:
ssh_args.append(arg)
continue
if arg in other_ssh_args:
if i != len(all_args) - 1:
raise SystemExit('Option {} cannot occur in the middle'.format(arg))
ssh_args.append(arg)
expecting_option_val = True
continue
raise SystemExit('Unknown option: {}'.format(arg))
continue
if expecting_option_val:
ssh_args.append(arg)
expecting_option_val = False
continue
server_args.append(arg)
if not server_args:
raise SystemExit('Must specify server to connect to')
return ssh_args, server_args, passthrough
def main(args):
server = args[1]
terminfo = subprocess.check_output(['infocmp']).decode('utf-8')
sh_script = SHELL_SCRIPT.replace('TERMINFO', terminfo, 1)
cmd = ['ssh', '-t', server, sh_script]
p = subprocess.Popen(cmd)
raise SystemExit(p.wait())
ssh_args, server_args, passthrough = parse_ssh_args(args[1:])
if passthrough:
cmd = ['ssh'] + ssh_args + server_args
else:
terminfo = subprocess.check_output(['infocmp']).decode('utf-8')
sh_script = SHELL_SCRIPT.replace('TERMINFO', terminfo, 1)
if len(server_args) > 1:
command_to_execute = ["'{}'".format(c.replace("'", """'"'"'""")) for c in server_args[1:]]
command_to_execute = 'cmd=({}); exec "$cmd"'.format(' '.join(command_to_execute))
else:
command_to_execute = ''
sh_script = sh_script.replace('EXEC_CMD', command_to_execute)
cmd = ['ssh'] + ssh_args + ['-t', server_args[0], sh_script] + server_args[1:]
os.execvp('ssh', cmd)
if __name__ == '__main__':

View File

@@ -9,7 +9,7 @@ from base64 import standard_b64encode
from collections import defaultdict, deque
from itertools import count
from kitty.utils import fit_image, screen_size_function
from kitty.utils import fit_image
from .operations import cursor
@@ -18,7 +18,6 @@ try:
codecs.lookup(fsenc)
except Exception:
fsenc = 'utf-8'
screen_size = screen_size_function()
class ImageData:
@@ -123,6 +122,10 @@ class ImageManager:
def next_image_id(self):
return next(self.image_id_counter) + 2
@property
def screen_size(self):
return self.handler.screen_size
def __enter__(self):
import tempfile
self.tdir = tempfile.mkdtemp(prefix='kitten-images-')
@@ -190,7 +193,7 @@ class ImageManager:
self.failed_images[path] = e
raise
m = self.image_data[path]
ss = screen_size()
ss = self.screen_size
if max_cols is None:
max_cols = ss.cols
if max_rows is None:

128
kittens/tui/line_edit.py Normal file
View File

@@ -0,0 +1,128 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
from kitty.fast_data_types import truncate_point_for_length, wcswidth
from kitty.key_encoding import RELEASE, HOME, END, BACKSPACE, DELETE, LEFT, RIGHT
class LineEdit:
def __init__(self):
self.clear()
def clear(self):
self.current_input = ''
self.cursor_pos = 0
self.pending_bell = False
def split_at_cursor(self, delta=0):
pos = max(0, self.cursor_pos + delta)
x = truncate_point_for_length(self.current_input, pos) if pos else 0
before, after = self.current_input[:x], self.current_input[x:]
return before, after
def write(self, write, prompt=''):
if self.pending_bell:
write('\a')
self.pending_bell = False
write(prompt)
write(self.current_input)
write('\r\x1b[{}C'.format(self.cursor_pos + wcswidth(prompt)))
def add_text(self, text):
if self.current_input:
x = truncate_point_for_length(self.current_input, self.cursor_pos) if self.cursor_pos else 0
self.current_input = self.current_input[:x] + text + self.current_input[x:]
else:
self.current_input = text
self.cursor_pos += wcswidth(text)
def on_text(self, text, in_bracketed_paste):
self.add_text(text)
def backspace(self, num=1):
before, after = self.split_at_cursor()
nbefore = before[:-num]
if nbefore != before:
self.current_input = nbefore + after
self.cursor_pos = wcswidth(nbefore)
return True
self.pending_bell = True
return False
def delete(self, num=1):
before, after = self.split_at_cursor()
nafter = after[num:]
if nafter != after:
self.current_input = before + nafter
self.cursor_pos = wcswidth(before)
return True
self.pending_bell = True
return False
def _left(self):
if not self.current_input:
self.cursor_pos = 0
return
if self.cursor_pos:
before, after = self.split_at_cursor(-1)
self.cursor_pos = wcswidth(before)
def _right(self):
if not self.current_input:
self.cursor_pos = 0
return
max_pos = wcswidth(self.current_input)
if self.cursor_pos >= max_pos:
self.cursor_pos = max_pos
return
before, after = self.split_at_cursor(1)
self.cursor_pos += 1 + int(wcswidth(before) == self.cursor_pos)
def _move_loop(self, func, num):
before = self.cursor_pos
while func() and num > 0:
num -= 1
changed = self.cursor_pos != before
if not changed:
self.pending_bell = True
return changed
def left(self, num=1):
return self._move_loop(self._left, num)
def right(self, num=1):
return self._move_loop(self._right, num)
def home(self):
if self.cursor_pos:
self.cursor_pos = 0
return True
return False
def end(self):
orig = self.cursor_pos
self.cursor_pos = wcswidth(self.current_input) + 1
return self.cursor_pos != orig
def on_key(self, key_event):
if key_event.type is RELEASE:
return False
elif key_event.key is HOME:
return self.home()
elif key_event.key is END:
return self.end()
elif key_event.key is BACKSPACE:
self.backspace()
return True
elif key_event.key is DELETE:
self.delete()
return True
elif key_event.key is LEFT:
self.left()
return True
elif key_event.key is RIGHT:
self.right()
return True
return False

View File

@@ -3,32 +3,30 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import codecs
import fcntl
import io
import os
import re
import selectors
import signal
import sys
import termios
import tty
from collections import namedtuple
from contextlib import closing, contextmanager
from functools import partial
from queue import Empty, Queue
from kitty.fast_data_types import parse_input_from_terminal, safe_pipe
from kitty.fast_data_types import (
close_tty, normal_tty, open_tty, parse_input_from_terminal, raw_tty,
safe_pipe
)
from kitty.key_encoding import (
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, C, D, backspace_key,
decode_key_event, enter_key
)
from kitty.utils import screen_size_function
from kitty.utils import screen_size_function, write_all
from .handler import Handler
from .operations import init_state, reset_state
screen_size = screen_size_function()
def debug(*a, **kw):
from base64 import standard_b64encode
@@ -43,40 +41,21 @@ def debug(*a, **kw):
fobj.flush()
def write_all(fd, data):
if isinstance(data, str):
data = data.encode('utf-8')
while data:
n = os.write(fd, data)
if not n:
break
data = data[n:]
class TermManager:
def __init__(self, input_fd, output_fd):
self.input_fd = input_fd
self.output_fd = output_fd
self.original_fl = fcntl.fcntl(self.input_fd, fcntl.F_GETFL)
def __init__(self):
self.extra_finalize = None
self.isatty = os.isatty(self.input_fd)
if self.isatty:
self.original_termios = termios.tcgetattr(self.input_fd)
def set_state_for_loop(self):
write_all(self.output_fd, init_state())
fcntl.fcntl(self.input_fd, fcntl.F_SETFL, self.original_fl | os.O_NONBLOCK)
if self.isatty:
tty.setraw(self.input_fd)
def set_state_for_loop(self, set_raw=True):
if set_raw:
raw_tty(self.tty_fd, self.original_termios)
write_all(self.tty_fd, init_state())
def reset_state_to_original(self):
if self.isatty:
termios.tcsetattr(self.input_fd, termios.TCSADRAIN, self.original_termios)
fcntl.fcntl(self.input_fd, fcntl.F_SETFL, self.original_fl)
normal_tty(self.tty_fd, self.original_termios)
if self.extra_finalize:
write_all(self.output_fd, self.extra_finalize)
write_all(self.output_fd, reset_state())
write_all(self.tty_fd, self.extra_finalize)
write_all(self.tty_fd, reset_state())
@contextmanager
def suspend(self):
@@ -85,11 +64,37 @@ class TermManager:
self.set_state_for_loop()
def __enter__(self):
self.set_state_for_loop()
self.tty_fd, self.original_termios = open_tty()
self.set_state_for_loop(set_raw=False)
return self
def __exit__(self, *a):
self.reset_state_to_original()
close_tty(self.tty_fd, self.original_termios)
del self.tty_fd, self.original_termios
class SignalManager:
def __init__(self, on_sigwinch, on_sigterm, on_sigint):
self.on_sigwinch = on_sigwinch
self.on_sigterm = on_sigterm
self.on_sigint = on_sigint
def __enter__(self):
for x in ('winch', 'term', 'int'):
attr = 'on_sig' + x
handler = getattr(self, attr)
old_handler = signal.signal(getattr(signal, 'SIG' + x.upper()), handler)
setattr(self, attr, old_handler)
def __exit__(self, *a):
for x in ('winch', 'term', 'int'):
attr = 'on_sig' + x
val = getattr(self, attr)
if val is None:
val = signal.SIG_DFL
signal.signal(getattr(signal, 'SIG' + x.upper()), val)
LEFT, MIDDLE, RIGHT, FOURTH, FIFTH = 1, 2, 4, 8, 16
@@ -152,19 +157,13 @@ class UnhandledException(Handler):
class Loop:
def __init__(self, input_fd=None, output_fd=None,
def __init__(self,
sanitize_bracketed_paste='[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'):
self.input_fd = sys.stdin.fileno() if input_fd is None else input_fd
self.output_fd = sys.stdout.fileno() if output_fd is None else output_fd
self.wakeup_read_fd, self.wakeup_write_fd = safe_pipe()
# For some reason on macOS the DefaultSelector fails when input_fd is
# For some reason on macOS the DefaultSelector fails when tty_fd is
# open('/dev/tty')
self.sel = s = selectors.PollSelector()
s.register(self.input_fd, selectors.EVENT_READ, self._read_ready)
s.register(
self.wakeup_read_fd, selectors.EVENT_READ, self._wakeup_ready
)
s.register(self.output_fd, selectors.EVENT_WRITE, self._write_ready)
s.register(self.wakeup_read_fd, selectors.EVENT_READ)
self.return_code = 0
self.read_allowed = True
self.read_buf = ''
@@ -198,11 +197,11 @@ class Loop:
self.jobs_queue.put(entry)
self._wakeup_write(b'j')
def _read_ready(self, handler):
def _read_ready(self, handler, fd):
if not self.read_allowed:
return
try:
data = os.read(self.input_fd, io.DEFAULT_BUFFER_SIZE)
data = os.read(fd, io.DEFAULT_BUFFER_SIZE)
except BlockingIOError:
return
if not data:
@@ -236,7 +235,7 @@ class Loop:
self.handler.on_eot()
else:
self.handler.on_text(chunk, self.in_bracketed_paste)
else:
elif chunk:
self.handler.on_text(chunk, self.in_bracketed_paste)
def _on_dcs(self, dcs):
@@ -294,12 +293,15 @@ class Loop:
if self.handler.image_manager is not None:
self.handler.image_manager.handle_response(apc)
def _write_ready(self, handler):
def _write_ready(self, handler, fd):
if len(handler.write_buf) > self.iov_limit:
handler.write_buf[self.iov_limit - 1] = b''.join(handler.write_buf[self.iov_limit - 1:])
del handler.write_buf[self.iov_limit:]
sizes = tuple(map(len, handler.write_buf))
written = os.writev(self.output_fd, handler.write_buf)
try:
written = os.writev(fd, handler.write_buf)
except BlockingIOError:
return
if not written:
raise EOFError('The output stream is closed')
if written >= sum(sizes):
@@ -317,11 +319,11 @@ class Loop:
break
del handler.write_buf[:consumed]
def _wakeup_ready(self, handler):
data = os.read(self.wakeup_read_fd, 1024)
def _wakeup_ready(self, handler, fd):
data = os.read(fd, 1024)
if b'r' in data:
screen_size.changed = True
handler.on_resize(screen_size())
self._get_screen_size.changed = True
handler.on_resize(self._get_screen_size())
if b't' in data:
handler.on_term()
if b'i' in data:
@@ -359,63 +361,16 @@ class Loop:
def wakeup(self):
self._wakeup_write(b'1')
def _modify_output_selector(self, waiting_for_write):
def _modify_output_selector(self, tty_fd, waiting_for_write):
events = selectors.EVENT_READ
if waiting_for_write:
try:
self.sel.register(self.output_fd, selectors.EVENT_WRITE, self._write_ready)
except KeyError:
pass
else:
try:
self.sel.unregister(self.output_fd)
except KeyError:
pass
events |= selectors.EVENT_WRITE
self.sel.modify(tty_fd, events)
def loop(self, handler):
def loop_impl(self, handler, tty_fd, image_manager=None, waiting_for_write=True):
read_ready, write_ready, wakeup_ready = self._read_ready, self._write_ready, self._wakeup_ready
select = self.sel.select
tb = None
waiting_for_write = True
with closing(self.sel), TermManager(self.input_fd, self.output_fd) as term_manager:
signal.signal(signal.SIGWINCH, self._on_sigwinch)
signal.signal(signal.SIGTERM, self._on_sigterm)
signal.signal(signal.SIGINT, self._on_sigint)
handler.write_buf = []
handler._term_manager = term_manager
image_manager = None
if handler.image_manager_class is not None:
image_manager = handler.image_manager_class(handler)
keep_going = True
try:
handler._initialize(screen_size(), self.quit, self.wakeup, self.start_job, debug, image_manager)
with handler:
while keep_going:
has_data_to_write = bool(handler.write_buf)
if not has_data_to_write and not self.read_allowed:
break
if has_data_to_write != waiting_for_write:
waiting_for_write = has_data_to_write
self._modify_output_selector(waiting_for_write)
events = select()
for key, mask in events:
key.data(handler)
except Exception:
import traceback
tb = traceback.format_exc()
self.return_code = 1
keep_going = False
term_manager.extra_finalize = b''.join(handler.write_buf).decode('utf-8')
if tb is not None:
self._report_error_loop(tb, term_manager)
def _report_error_loop(self, tb, term_manager):
select = self.sel.select
waiting_for_write = False
handler = UnhandledException(tb)
handler.write_buf = []
handler._term_manager = term_manager
handler._initialize(screen_size(), self.quit, self.wakeup, self.start_job, debug)
handler._initialize(self._get_screen_size(), self.quit, self.wakeup, self.start_job, debug, image_manager)
with handler:
while True:
has_data_to_write = bool(handler.write_buf)
@@ -423,7 +378,41 @@ class Loop:
break
if has_data_to_write != waiting_for_write:
waiting_for_write = has_data_to_write
self._modify_output_selector(waiting_for_write)
events = select()
for key, mask in events:
key.data(handler)
self._modify_output_selector(tty_fd, waiting_for_write)
for key, mask in select():
fd = key.fd
if fd == tty_fd:
if mask & selectors.EVENT_READ:
read_ready(handler, fd)
if mask & selectors.EVENT_WRITE:
write_ready(handler, fd)
else:
wakeup_ready(handler, fd)
def loop(self, handler):
tb = None
signal_manager = SignalManager(self._on_sigwinch, self._on_sigterm, self._on_sigint)
with closing(self.sel), TermManager() as term_manager, signal_manager:
self.sel.register(term_manager.tty_fd, selectors.EVENT_READ | selectors.EVENT_WRITE)
self._get_screen_size = screen_size_function(term_manager.tty_fd)
handler.write_buf = []
handler._term_manager = term_manager
image_manager = None
if handler.image_manager_class is not None:
image_manager = handler.image_manager_class(handler)
try:
self.loop_impl(handler, term_manager.tty_fd, image_manager)
except Exception:
import traceback
tb = traceback.format_exc()
self.return_code = 1
term_manager.extra_finalize = b''.join(handler.write_buf).decode('utf-8')
if tb is not None:
self._report_error_loop(tb, term_manager)
def _report_error_loop(self, tb, term_manager):
handler = UnhandledException(tb)
handler.write_buf = []
handler._term_manager = term_manager
self.loop_impl(handler, term_manager.tty_fd, waiting_for_write=False)

View File

@@ -78,6 +78,13 @@ def set_cursor_position(x, y) -> str: # (0, 0) is top left
return '\033[{};{}H'.format(y + 1, x + 1)
def set_cursor_shape(shape='block', blink=True) -> str:
val = {'block': 1, 'underline': 3, 'bar': 5}.get(shape, 1)
if not blink:
val += 1
return '\033[{} q'.format(val)
def set_scrolling_region(screen_size=None, top=None, bottom=None) -> str:
if screen_size is None:
return '\033[r'
@@ -182,10 +189,11 @@ def init_state(alternate_screen=True):
reset_mode('IRM') + reset_mode('DECKM') + reset_mode('DECSCNM') +
set_mode('DECARM') + reset_mode('DECOM') + set_mode('DECAWM') +
set_mode('DECTCEM') + reset_mode('MOUSE_BUTTON_TRACKING') +
reset_mode('MOUSE_MOTION_TRACKING') + reset_mode('MOUSE_MOVE_TRACKING')
+ reset_mode('FOCUS_TRACKING') + reset_mode('MOUSE_UTF8_MODE') +
reset_mode('MOUSE_MOTION_TRACKING') + reset_mode('MOUSE_MOVE_TRACKING') +
reset_mode('FOCUS_TRACKING') + reset_mode('MOUSE_UTF8_MODE') +
reset_mode('MOUSE_SGR_MODE') + reset_mode('MOUSE_UTF8_MODE') +
set_mode('BRACKETED_PASTE') + set_mode('EXTENDED_KEYBOARD') +
'\033]30001\033\\' +
'\033[*x' # reset DECSACE to default region select
)
if alternate_screen:
@@ -200,6 +208,7 @@ def reset_state(normal_screen=True):
ans += reset_mode('ALTERNATE_SCREEN')
ans += RESTORE_PRIVATE_MODE_VALUES
ans += RESTORE_CURSOR
ans += '\033]30101\033\\'
return ans
@@ -218,16 +227,21 @@ def alternate_screen(f=None):
print(reset_mode('ALTERNATE_SCREEN'), end='', file=f)
def set_default_colors(fg=None, bg=None) -> str:
def set_default_colors(fg=None, bg=None, cursor=None, select_bg=None, select_fg=None) -> str:
ans = ''
if fg is None:
ans += '\x1b]110\x1b\\'
else:
ans += '\x1b]10;{}\x1b\\'.format(color_as_sharp(fg if isinstance(fg, Color) else to_color(fg)))
if bg is None:
ans += '\x1b]111\x1b\\'
else:
ans += '\x1b]11;{}\x1b\\'.format(color_as_sharp(bg if isinstance(bg, Color) else to_color(bg)))
def item(which, num):
nonlocal ans
if which is None:
ans += '\x1b]1{}\x1b\\'.format(num)
else:
ans += '\x1b]{};{}\x1b\\'.format(num, color_as_sharp(which if isinstance(which, Color) else to_color(which)))
item(fg, 10)
item(bg, 11)
item(cursor, 12)
item(select_bg, 17)
item(select_fg, 19)
return ans
@@ -235,7 +249,15 @@ def write_to_clipboard(data, use_primary=False) -> str:
if isinstance(data, str):
data = data.encode('utf-8')
from base64 import standard_b64encode
return '\x1b]52;{};{}\x07'.format('p' if use_primary else 'c', standard_b64encode(data).decode('ascii'))
fmt = 'p' if use_primary else 'c'
def esc(chunk):
return '\x1b]52;{};{}\x07'.format(fmt, chunk)
ans = esc('!') # clear clipboard buffer
for chunk in (data[i:i+512] for i in range(0, len(data), 512)):
chunk = standard_b64encode(chunk).decode('ascii')
ans += esc(chunk)
return ans
def request_from_clipboard(use_primary=False) -> str:

View File

@@ -9,13 +9,15 @@ from functools import lru_cache
from gettext import gettext as _
from kitty.config import cached_values_for
from kitty.constants import config_dir, editor
from kitty.constants import config_dir
from kitty.utils import get_editor
from kitty.fast_data_types import wcswidth
from kitty.key_encoding import (
DOWN, ESCAPE, F1, F2, F3, F4, F12, LEFT, RELEASE, RIGHT, SHIFT, TAB, UP,
backspace_key, enter_key
enter_key
)
from ..tui.line_edit import LineEdit
from ..tui.handler import Handler
from ..tui.loop import Loop
from ..tui.operations import (
@@ -246,8 +248,8 @@ class UnicodeInput(Handler):
def __init__(self, cached_values):
self.cached_values = cached_values
self.line_edit = LineEdit()
self.recent = list(self.cached_values.get('recent', DEFAULT_SET))
self.current_input = ''
self.current_char = None
self.prompt_template = '{}> '
self.last_updated_code_point_at = None
@@ -268,9 +270,9 @@ class UnicodeInput(Handler):
codepoints = load_favorites()
q = self.mode, tuple(codepoints)
elif self.mode is NAME:
q = self.mode, self.current_input
q = self.mode, self.line_edit.current_input
if q != self.last_updated_code_point_at:
words = self.current_input.split()
words = self.line_edit.current_input.split()
words = [w for w in words if w != INDEX_CHAR]
index_words = [i for i, w in enumerate(words) if i > 0 and is_index(w)]
if index_words:
@@ -290,10 +292,10 @@ class UnicodeInput(Handler):
self.current_char = None
if self.mode is HEX:
try:
if self.current_input.startswith(INDEX_CHAR) and len(self.current_input) > 1:
self.current_char = chr(self.table.codepoint_at_hint(self.current_input[1:]))
if self.line_edit.current_input.startswith(INDEX_CHAR) and len(self.line_edit.current_input) > 1:
self.current_char = chr(self.table.codepoint_at_hint(self.line_edit.current_input[1:]))
else:
code = int(self.current_input, 16)
code = int(self.line_edit.current_input, 16)
self.current_char = chr(code)
except Exception:
pass
@@ -303,8 +305,8 @@ class UnicodeInput(Handler):
self.current_char = chr(cc)
else:
try:
if self.current_input:
self.current_char = chr(self.table.codepoint_at_hint(self.current_input.lstrip(INDEX_CHAR)))
if self.line_edit.current_input:
self.current_char = chr(self.table.codepoint_at_hint(self.line_edit.current_input.lstrip(INDEX_CHAR)))
except Exception:
pass
if self.current_char is not None:
@@ -365,8 +367,7 @@ class UnicodeInput(Handler):
writeln(_('Enter the hex code for the character'))
else:
writeln(_('Enter the index for the character you want from the list below'))
self.write(self.prompt)
self.write(self.current_input)
self.line_edit.write(self.write, self.prompt)
with cursor(self.write):
writeln()
writeln(self.choice_line)
@@ -384,14 +385,34 @@ class UnicodeInput(Handler):
self.draw_screen()
def on_text(self, text, in_bracketed_paste):
self.current_input += text
self.line_edit.on_text(text, in_bracketed_paste)
self.refresh()
def on_key(self, key_event):
if key_event is backspace_key:
self.current_input = self.current_input[:-1]
if self.mode is NAME and key_event.type is not RELEASE and not key_event.mods:
if key_event.key is TAB:
if key_event.mods == SHIFT:
self.table.move_current(cols=-1), self.refresh()
elif not key_event.mods:
self.table.move_current(cols=1), self.refresh()
return
elif key_event.key is LEFT and not key_event.mods:
self.table.move_current(cols=-1), self.refresh()
return
elif key_event.key is RIGHT and not key_event.mods:
self.table.move_current(cols=1), self.refresh()
return
elif key_event.key is UP and not key_event.mods:
self.table.move_current(rows=-1), self.refresh()
return
elif key_event.key is DOWN and not key_event.mods:
self.table.move_current(rows=1), self.refresh()
return
if self.line_edit.on_key(key_event):
self.refresh()
elif key_event is enter_key:
return
if key_event is enter_key:
self.quit_loop(0)
elif key_event.type is RELEASE and not key_event.mods:
if key_event.key is ESCAPE:
@@ -406,27 +427,13 @@ class UnicodeInput(Handler):
self.switch_mode(FAVORITES)
elif key_event.key is F12 and self.mode is FAVORITES:
self.edit_favorites()
elif self.mode is NAME:
if key_event.key is TAB:
if key_event.mods == SHIFT:
self.table.move_current(cols=-1), self.refresh()
elif not key_event.mods:
self.table.move_current(cols=1), self.refresh()
elif key_event.key is LEFT and not key_event.mods:
self.table.move_current(cols=-1), self.refresh()
elif key_event.key is RIGHT and not key_event.mods:
self.table.move_current(cols=1), self.refresh()
elif key_event.key is UP and not key_event.mods:
self.table.move_current(rows=-1), self.refresh()
elif key_event.key is DOWN and not key_event.mods:
self.table.move_current(rows=1), self.refresh()
def edit_favorites(self):
if not os.path.exists(favorites_path):
with open(favorites_path, 'wb') as f:
f.write(serialize_favorites(load_favorites()).encode('utf-8'))
with self.suspend():
p = subprocess.Popen(editor + [favorites_path])
p = subprocess.Popen(get_editor() + [favorites_path])
if p.wait() == 0:
load_favorites(refresh=True)
self.init_terminal_state()
@@ -436,7 +443,7 @@ class UnicodeInput(Handler):
if mode is not self.mode:
self.mode = mode
self.cached_values['mode'] = mode
self.current_input = ''
self.line_edit.clear()
self.current_char = None
self.choice_line = ''
self.refresh()

File diff suppressed because one or more lines are too long

View File

@@ -6,8 +6,7 @@ from functools import partial
from itertools import chain
from .fast_data_types import (
BORDERS_PROGRAM, add_borders_rect, compile_program, init_borders_program,
pt_to_px
BORDERS_PROGRAM, add_borders_rect, compile_program, init_borders_program
)
from .utils import load_shaders
@@ -39,11 +38,11 @@ def load_borders_program():
class Borders:
def __init__(self, os_window_id, tab_id, opts):
def __init__(self, os_window_id, tab_id, opts, border_width, padding_width):
self.os_window_id = os_window_id
self.tab_id = tab_id
self.border_width = pt_to_px(opts.window_border_width)
self.padding_width = pt_to_px(opts.window_padding_width)
self.border_width = border_width
self.padding_width = padding_width
def __call__(
self,

View File

@@ -11,39 +11,31 @@ from gettext import gettext as _
from weakref import WeakValueDictionary
from .cli import create_opts, parse_args
from .config import (
MINIMUM_FONT_SIZE, initial_window_size, prepare_config_file_for_editing
)
from .config_utils import to_cmdline
from .conf.utils import to_cmdline
from .config import initial_window_size_func, prepare_config_file_for_editing
from .config_data import MINIMUM_FONT_SIZE
from .constants import (
appname, config_dir, editor, set_boss, supports_primary_selection
appname, config_dir, set_boss, supports_primary_selection
)
from .fast_data_types import (
ChildMonitor, background_opacity_of, change_background_opacity,
create_os_window, current_os_window, destroy_global_data,
destroy_sprite_map, get_clipboard_string, glfw_post_empty_event,
layout_sprite_map, mark_os_window_for_close, set_clipboard_string,
set_dpi_from_os_window, set_in_sequence_mode, show_window,
toggle_fullscreen, viewport_for_window
get_clipboard_string, glfw_post_empty_event, global_font_size,
mark_os_window_for_close, os_window_font_size, patch_global_colors,
set_clipboard_string, set_in_sequence_mode, toggle_fullscreen
)
from .fonts.render import prerender, resize_fonts, set_font_family
from .keys import get_shortcut, shortcut_matches
from .remote_control import handle_cmd
from .rgb import Color, color_from_int
from .session import create_session
from .tabs import SpecialWindow, SpecialWindowInstance, TabManager
from .utils import (
end_startup_notification, get_primary_selection, init_startup_notification,
log_error, open_url, parse_address_spec, remove_socket_file, safe_print,
set_primary_selection, single_instance
get_editor, get_primary_selection, log_error, open_url, parse_address_spec,
remove_socket_file, safe_print, set_primary_selection, single_instance,
startup_notification_handler
)
def initialize_renderer():
layout_sprite_map()
prerender()
def listen_on(spec):
import socket
family, address, socket_path = parse_address_spec(spec)
@@ -83,12 +75,13 @@ class DumpCommands: # {{{
class Boss:
def __init__(self, os_window_id, opts, args, cached_values):
def __init__(self, os_window_id, opts, args, cached_values, new_os_window_trigger):
self.window_id_map = WeakValueDictionary()
self.startup_colors = {k: opts[k] for k in opts if isinstance(opts[k], Color)}
self.pending_sequences = None
self.cached_values = cached_values
self.os_window_map = {}
self.os_window_death_actions = {}
self.cursor_blinking = True
self.shutting_down = False
talk_fd = getattr(single_instance, 'socket', None)
@@ -102,28 +95,24 @@ class Boss:
talk_fd, listen_fd
)
set_boss(self)
self.current_font_size = opts.font_size
set_font_family(opts)
self.opts, self.args = opts, args
initialize_renderer()
startup_session = create_session(opts, args)
startup_session = create_session(opts, args, default_session=opts.startup_session)
self.keymap = self.opts.keymap.copy()
if new_os_window_trigger is not None:
self.keymap.pop(new_os_window_trigger, None)
self.add_os_window(startup_session, os_window_id=os_window_id)
def add_os_window(self, startup_session, os_window_id=None, wclass=None, wname=None, size=None, startup_id=None):
dpi_changed = False
def add_os_window(self, startup_session, os_window_id=None, wclass=None, wname=None, opts_for_size=None, startup_id=None):
if os_window_id is None:
w, h = initial_window_size(self.opts, self.cached_values) if size is None else size
opts_for_size = opts_for_size or self.opts
cls = wclass or self.args.cls or appname
os_window_id = create_os_window(w, h, appname, wname or self.args.name or cls, cls)
if startup_id:
ctx = init_startup_notification(os_window_id, startup_id)
dpi_changed = show_window(os_window_id)
if startup_id:
end_startup_notification(ctx)
with startup_notification_handler(do_notify=startup_id is not None, startup_id=startup_id) as pre_show_callback:
os_window_id = create_os_window(
initial_window_size_func(opts_for_size, self.cached_values),
pre_show_callback,
appname, wname or self.args.name or cls, cls)
tm = TabManager(os_window_id, self.opts, self.args, startup_session)
self.os_window_map[os_window_id] = tm
if dpi_changed:
self.on_dpi_change(os_window_id)
return os_window_id
def list_os_windows(self):
@@ -251,7 +240,9 @@ class Boss:
if not os.path.isabs(args.directory):
args.directory = os.path.join(msg['cwd'], args.directory)
session = create_session(opts, args, respect_cwd=True)
self.add_os_window(session, wclass=args.cls, wname=args.name, size=initial_window_size(opts, self.cached_values), startup_id=startup_id)
os_window_id = self.add_os_window(session, wclass=args.cls, wname=args.name, opts_for_size=opts, startup_id=startup_id)
if msg.get('notify_on_os_window_death'):
self.os_window_death_actions[os_window_id] = partial(self.notify_on_os_window_death, msg['notify_on_os_window_death'])
else:
log_error('Unknown message received from peer, ignoring')
@@ -315,52 +306,76 @@ class Boss:
tm.activate_tab_at(x)
def on_window_resize(self, os_window_id, w, h, dpi_changed):
tm = self.os_window_map.get(os_window_id)
if tm is not None:
if dpi_changed:
if set_dpi_from_os_window(os_window_id):
self.on_dpi_change(os_window_id)
else:
tm.resize()
else:
if dpi_changed:
self.on_dpi_change(os_window_id)
else:
tm = self.os_window_map.get(os_window_id)
if tm is not None:
tm.resize()
def increase_font_size(self):
self.set_font_size(
min(
self.opts.font_size * 5, self.current_font_size +
self.opts.font_size_delta))
def increase_font_size(self): # legacy
cfs = global_font_size()
self.set_font_size(min(self.opts.font_size * 5, cfs + 2.0))
def decrease_font_size(self):
self.set_font_size(self.current_font_size - self.opts.font_size_delta)
def decrease_font_size(self): # legacy
cfs = global_font_size()
self.set_font_size(max(MINIMUM_FONT_SIZE, cfs - 2.0))
def restore_font_size(self):
def restore_font_size(self): # legacy
self.set_font_size(self.opts.font_size)
def _change_font_size(self, new_size=None, on_dpi_change=False):
if new_size is not None:
self.current_font_size = new_size
old_cell_width, old_cell_height = viewport_for_window()[-2:]
windows = tuple(filter(None, self.window_id_map.values()))
resize_fonts(self.current_font_size, on_dpi_change=on_dpi_change)
layout_sprite_map()
prerender()
for window in windows:
window.screen.rescale_images(old_cell_width, old_cell_height)
window.screen.refresh_sprite_positions()
for tm in self.os_window_map.values():
tm.resize()
tm.refresh_sprite_positions()
glfw_post_empty_event()
def set_font_size(self, new_size): # legacy
self.change_font_size(True, None, new_size)
def set_font_size(self, new_size):
new_size = max(MINIMUM_FONT_SIZE, new_size)
if new_size == self.current_font_size:
return
self._change_font_size(new_size)
def change_font_size(self, all_windows, increment_operation, amt):
def calc_new_size(old_size):
new_size = old_size
if amt == 0:
new_size = self.opts.font_size
else:
if increment_operation:
new_size += (1 if increment_operation == '+' else -1) * amt
else:
new_size = amt
new_size = max(MINIMUM_FONT_SIZE, min(new_size, self.opts.font_size * 5))
return new_size
if all_windows:
current_global_size = global_font_size()
new_size = calc_new_size(current_global_size)
if new_size != current_global_size:
global_font_size(new_size)
os_windows = tuple(self.os_window_map.keys())
else:
os_windows = []
w = self.active_window
if w is not None:
os_windows.append(w.os_window_id)
if os_windows:
final_windows = {}
for wid in os_windows:
current_size = os_window_font_size(wid)
if current_size:
new_size = calc_new_size(current_size)
if new_size != current_size:
final_windows[wid] = new_size
if final_windows:
self._change_font_size(final_windows)
def _change_font_size(self, sz_map):
for os_window_id, sz in sz_map.items():
tm = self.os_window_map.get(os_window_id)
if tm is not None:
os_window_font_size(os_window_id, sz)
tm.resize()
def on_dpi_change(self, os_window_id):
self._change_font_size()
tm = self.os_window_map.get(os_window_id)
if tm is not None:
sz = os_window_font_size(os_window_id)
if sz:
os_window_font_size(os_window_id, sz, True)
tm.resize()
def _set_os_window_background_opacity(self, os_window_id, opacity):
change_background_opacity(os_window_id, max(0.1, min(opacity, 1.0)))
@@ -404,7 +419,7 @@ class Boss:
def dispatch_special_key(self, key, scancode, action, mods):
# Handles shortcuts, return True if the key was consumed
key_action = get_shortcut(self.opts.keymap, mods, key, scancode)
key_action = get_shortcut(self.keymap, mods, key, scancode)
if key_action is None:
sequences = get_shortcut(self.opts.sequence_map, mods, key, scancode)
if sequences:
@@ -517,19 +532,36 @@ class Boss:
tm.destroy()
for window_id in tuple(w.id for w in self.window_id_map.values() if getattr(w, 'os_window_id', None) == os_window_id):
self.window_id_map.pop(window_id, None)
action = self.os_window_death_actions.pop(os_window_id, None)
if action is not None:
action()
def display_scrollback(self, window, data):
def notify_on_os_window_death(self, address):
import socket
s = socket.socket(family=socket.AF_UNIX)
try:
s.connect(address)
s.sendall(b'c')
try:
s.shutdown(socket.SHUT_RDWR)
except EnvironmentError:
pass
s.close()
except Exception:
pass
def display_scrollback(self, window, data, cmd):
tab = self.active_tab
if tab is not None and window.overlay_for is None:
tab.new_special_window(
SpecialWindow(
self.opts.scrollback_pager, data, _('History'), overlay_for=window.id))
cmd, data, _('History'), overlay_for=window.id))
def edit_config_file(self, *a):
confpath = prepare_config_file_for_editing()
# On macOS vim fails to handle SIGWINCH if it occurs early, so add a
# small delay.
cmd = ['kitty', '+runpy', 'import os, sys, time; time.sleep(0.05); os.execvp(sys.argv[1], sys.argv[1:])'] + editor + [confpath]
cmd = ['kitty', '+runpy', 'import os, sys, time; time.sleep(0.05); os.execvp(sys.argv[1], sys.argv[1:])'] + get_editor() + [confpath]
self.new_os_window(*cmd)
def get_output(self, source_window, num_lines=1):
@@ -653,7 +685,6 @@ class Boss:
for tm in self.os_window_map.values():
tm.destroy()
self.os_window_map = {}
destroy_sprite_map()
destroy_global_data()
def paste_to_active_window(self, text):
@@ -785,3 +816,4 @@ class Boss:
setattr(self.opts, k, color_from_int(v))
for tm in self.all_tab_managers:
tm.tab_bar.patch_colors(spec)
patch_global_colors(spec, configured)

View File

@@ -5,9 +5,10 @@
* Distributed under terms of the GPL3 license.
*/
#include "threading.h"
#include "state.h"
#include "threading.h"
#include "screen.h"
#include "fonts.h"
#include <termios.h>
#include <unistd.h>
#include <float.h>
@@ -110,16 +111,22 @@ handle_signal(int sig_num) {
static inline bool
self_pipe(int fds[2]) {
#ifdef __APPLE__
int flags;
flags = pipe(fds);
if (flags != 0) return false;
flags = fcntl(fds[0], F_GETFD);
if (flags == -1) { return false; }
if (fcntl(fds[0], F_SETFD, flags | FD_CLOEXEC) == -1) { return false; }
flags = fcntl(fds[0], F_GETFL);
if (flags == -1) { return false; }
if (fcntl(fds[0], F_SETFL, flags | O_NONBLOCK) == -1) { return false; }
for (int i = 0; i < 2; i++) {
flags = fcntl(fds[i], F_GETFD);
if (flags == -1) { return false; }
if (fcntl(fds[i], F_SETFD, flags | FD_CLOEXEC) == -1) { return false; }
flags = fcntl(fds[i], F_GETFL);
if (flags == -1) { return false; }
if (fcntl(fds[i], F_SETFL, flags | O_NONBLOCK) == -1) { return false; }
}
return true;
#else
return pipe2(fds, O_CLOEXEC | O_NONBLOCK) == 0;
#endif
}
@@ -486,7 +493,7 @@ static double last_render_at = -DBL_MAX;
static inline double
cursor_width(double w, bool vert, OSWindow *os_window) {
double dpi = vert ? global_state.logical_dpi_x : global_state.logical_dpi_y;
double dpi = vert ? os_window->fonts_data->logical_dpi_x : os_window->fonts_data->logical_dpi_y;
double ans = w * dpi / 72.0; // as pixels
double factor = 2.0 / (vert ? os_window->viewport_width : os_window->viewport_height);
return ans * factor;
@@ -552,20 +559,6 @@ update_window_title(Window *w, OSWindow *os_window) {
return false;
}
static PyObject*
simple_render_screen(PyObject UNUSED *self, PyObject *args) {
#define simple_render_screen_doc "Render a Screen object, with no cursor"
Screen *screen;
float xstart, ystart, dx, dy;
static ssize_t vao_idx = -1, gvao_idx = -1;
if (vao_idx == -1) vao_idx = create_cell_vao();
if (gvao_idx == -1) gvao_idx = create_graphics_vao();
if (!PyArg_ParseTuple(args, "O!ffff", &Screen_Type, &screen, &xstart, &ystart, &dx, &dy)) return NULL;
send_cell_data_to_gpu(vao_idx, gvao_idx, xstart, ystart, dx, dy, screen, current_os_window());
draw_cells(vao_idx, gvao_idx, xstart, ystart, dx, dy, screen, current_os_window(), true);
Py_RETURN_NONE;
}
static inline bool
prepare_to_render_os_window(OSWindow *os_window, double now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows) {
#define TD os_window->tab_bar_render_data
@@ -615,12 +608,12 @@ render_os_window(OSWindow *os_window, double now, unsigned int active_window_id,
Tab *tab = os_window->tabs + os_window->active_tab;
BorderRects *br = &tab->border_rects;
draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, os_window->viewport_width, os_window->viewport_height, active_window_bg, num_visible_windows, os_window);
if (TD.screen && os_window->num_tabs > 1) draw_cells(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window, true);
if (TD.screen && os_window->num_tabs > 1) draw_cells(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window, true, false);
for (unsigned int i = 0; i < tab->num_windows; i++) {
Window *w = tab->windows + i;
if (w->visible && WD.screen) {
bool is_active_window = i == tab->active_window;
draw_cells(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window, is_active_window);
draw_cells(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window, is_active_window, true);
if (is_active_window && WD.screen->cursor_render_info.is_visible && (!WD.screen->cursor_render_info.is_focused || WD.screen->cursor_render_info.shape != CURSOR_BLOCK)) {
draw_cursor(&WD.screen->cursor_render_info, os_window->is_focused);
}
@@ -661,6 +654,7 @@ render(double now) {
}
unsigned int active_window_id = 0, num_visible_windows = 0;
color_type active_window_bg = 0;
if (!w->fonts_data) { log_error("No fonts data found for window id: %llu", w->id); continue; }
if (prepare_to_render_os_window(w, now, &active_window_id, &active_window_bg, &num_visible_windows)) needs_render = true;
if (w->last_active_window_id != active_window_id || w->last_active_tab != w->active_tab || w->focused_at_last_render != w->is_focused) needs_render = true;
if (needs_render) render_os_window(w, now, active_window_id, active_window_bg, num_visible_windows);
@@ -760,9 +754,26 @@ process_pending_closes(ChildMonitor *self) {
remove_os_window(os_window->id);
} else has_open_windows = true;
}
#ifdef __APPLE__
if (!OPT(macos_quit_when_last_window_closed)) {
if (!has_open_windows && !application_quit_requested()) has_open_windows = true;
}
#endif
return has_open_windows;
}
#ifdef __APPLE__
// If we create new OS windows during wait_events(), using global menu actions
// via the mouse causes a crash because of the way autorelease pools work in
// glfw/cocoa. So we use a flag instead.
static unsigned int cocoa_pending_actions = 0;
void
set_cocoa_pending_action(CocoaPendingAction action) {
cocoa_pending_actions |= action;
}
#endif
static PyObject*
main_loop(ChildMonitor *self, PyObject *a UNUSED) {
#define main_loop_doc "The main thread loop"
@@ -773,6 +784,13 @@ main_loop(ChildMonitor *self, PyObject *a UNUSED) {
if (global_state.has_pending_resizes) process_pending_resizes(now);
render(now);
wait_for_events();
#ifdef __APPLE__
if (cocoa_pending_actions) {
if (cocoa_pending_actions & PREFERENCES_WINDOW) { call_boss(edit_config_file, NULL); }
if (cocoa_pending_actions & NEW_OS_WINDOW) { call_boss(new_os_window, NULL); }
cocoa_pending_actions = 0;
}
#endif
parse_input(self);
if (global_state.close_all_windows) close_all_windows();
has_open_windows = process_pending_closes(self);
@@ -1330,7 +1348,6 @@ safe_pipe(PYNOARG) {
}
static PyMethodDef module_methods[] = {
METHOD(simple_render_screen, METH_VARARGS)
METHODB(safe_pipe, METH_NOARGS),
{NULL} /* Sentinel */
};

View File

@@ -23,7 +23,7 @@ def cmdline_of_process(pid):
if is_macos:
# TODO: macOS implementation, see DarwinProcess.c in htop for inspiration
raise NotImplementedError()
return open('/proc/{}/cmdline'.format(pid), 'rb').read().decode('utf-8').split('\0')
return list(filter(None, open('/proc/{}/cmdline'.format(pid), 'rb').read().decode('utf-8').split('\0')))
def remove_cloexec(fd):
@@ -88,7 +88,7 @@ class Child:
@property
def cmdline(self):
try:
return cmdline_of_process(self.pid)
return cmdline_of_process(self.pid) or list(self.argv)
except Exception:
return list(self.argv)

View File

@@ -8,7 +8,7 @@ import sys
from collections import deque
from .config import defaults, load_config
from .config_utils import resolve_config
from .conf.utils import resolve_config
from .constants import appname, defconf, is_macos, is_wayland, str_version
CONFIG_HELP = '''\
@@ -19,29 +19,29 @@ sequence, which are merged. Use the special value NONE to not load a config
file.
If this option is not specified, config files are searched for in the order:
"$XDG_CONFIG_HOME/{appname}/{conf_name}.conf", "~/.config/{appname}/{conf_name}.conf", {macos_confpath}
"$XDG_CONFIG_DIRS/{appname}/{conf_name}.conf". The first one that exists is used as the
:file:`$XDG_CONFIG_HOME/{appname}/{conf_name}.conf`, :file:`~/.config/{appname}/{conf_name}.conf`, {macos_confpath}
:file:`$XDG_CONFIG_DIRS/{appname}/{conf_name}.conf`. The first one that exists is used as the
config file.
If the environment variable "KITTY_CONFIG_DIRECTORY" is specified, that
If the environment variable :env:`KITTY_CONFIG_DIRECTORY` is specified, that
directory is always used and the above searching does not happen.
If "/etc/xdg/{appname}/{conf_name}.conf" exists it is merged before (i.e. with lower
If :file:`/etc/xdg/{appname}/{conf_name}.conf` exists it is merged before (i.e. with lower
priority) than any user config files. It can be used to specify system-wide
defaults for all users.
'''.replace('{macos_confpath}', '~/Library/Preferences/{appname}/{conf_name}.conf, ' if is_macos else '')
'''.replace('{macos_confpath}', ':file:`~/Library/Preferences/{appname}/{conf_name}.conf`,' if is_macos else '')
OPTIONS = '''
--class
dest=cls
default={appname}
condition=not is_macos
Set the class part of the |_ WM_CLASS| window property
Set the class part of the :italic:`WM_CLASS` window property
--name
condition=not is_macos
Set the name part of the |_ WM_CLASS| property (defaults to using the value from |_ --class|)
Set the name part of the :italic:`WM_CLASS` property (defaults to using the value from :option:`{appname} --class`)
--title
@@ -53,10 +53,11 @@ only use this if you are running a program that does not set titles.
type=list
{config_help}
--override -o
type=list
Override individual configuration options, can be specified multiple times.
Syntax: |_ name=value|. For example: |_ -o font_size=20|
Syntax: :italic:`name=value`. For example: :option:`kitty -o` font_size=20
--directory -d
@@ -71,38 +72,47 @@ Detach from the controlling terminal, if any
--session
Path to a file containing the startup |_ session| (tabs, windows, layout, programs).
Path to a file containing the startup :italic:`session` (tabs, windows, layout, programs).
See the README file for details and an example.
--single-instance -1
type=bool-set
If specified only a single instance of |_ {appname}| will run. New invocations will
instead create a new top-level window in the existing |_ {appname}| instance. This
allows |_ {appname}| to share a single sprite cache on the GPU and also reduces
startup time. You can also have separate groups of |_ {appname}| instances by using the
|_ --instance-group| option
If specified only a single instance of :italic:`{appname}` will run. New invocations will
instead create a new top-level window in the existing :italic:`{appname}` instance. This
allows :italic:`{appname}` to share a single sprite cache on the GPU and also reduces
startup time. You can also have separate groups of :italic:`{appname}` instances by using the
:option:`kitty --instance-group` option
--instance-group
Used in combination with the |_ --single-instance| option. All |_ {appname}| invocations
with the same |_ --instance-group| will result in new windows being created
in the first |_ {appname}| instance within that group
Used in combination with the :option:`kitty --single-instance` option. All :italic:`{appname}` invocations
with the same :option:`kitty --instance-group` will result in new windows being created
in the first :italic:`{appname}` instance within that group
--wait-for-single-instance-window-close
type=bool-set
Normally, when using :option:`--single-instance`, :italic:`{appname}` will open a new window in an existing
instance and quit immediately. With this option, it will not quit till the newly opened
window is closed. Note that if no previous instance is found, then :italic:`{appname}` will wait anyway,
regardless of this option.
--listen-on
Tell kitty to listen on the specified address for control
messages. For example, |_ --listen-on=unix:/tmp/mykitty| or
|_ --listen-on=tcp:localhost:12345|. On Linux systems, you can also use abstract
UNIX sockets, not associated with a file, like this: |_ --listen-on=unix:@mykitty|.
To control kitty, you can send it commands with |_ kitty @| using the |_ --to| option
messages. For example, :option:`{appname} --listen-on`=unix:/tmp/mykitty or
:option:`{appname} --listen-on`=tcp:localhost:12345. On Linux systems, you can also use abstract
UNIX sockets, not associated with a file, like this: :option:`{appname} --listen-on`=unix:@mykitty.
To control kitty, you can send it commands with :italic:`kitty @` using the :option:`kitty @ --to` option
to specify this address. Note that this option will be ignored, unless you set
|_ allow_remote_control| to yes in |_ kitty.conf|
:opt:`allow_remote_control` to yes in :file:`kitty.conf`.
# Debugging options
--version -v
type=bool-set
The current {appname} version
@@ -113,7 +123,7 @@ Output commands received from child process to stdout
--replay-commands
Replay previously dumped commands. Specify the path to a dump file previously created by --dump-commands. You
can open a new kitty window to replay the commands with:
can open a new kitty window to replay the commands with::
kitty sh -c "kitty --replay-commands /path/to/dump/file; read"
@@ -124,7 +134,13 @@ Path to file in which to store the raw bytes received from the child process
--debug-gl
type=bool-set
Debug OpenGL commands. This will cause all OpenGL calls to check for errors instead of ignoring them. Useful when debugging rendering problems
Debug OpenGL commands. This will cause all OpenGL calls to check for errors
instead of ignoring them. Useful when debugging rendering problems
--debug-keyboard
type=bool-set
This option will cause kitty to print out key events as they are received
--debug-font-fallback
@@ -181,6 +197,30 @@ def title(x):
return blue(bold(x))
def opt(text):
return text
def option(x):
idx = x.find('-')
if idx > -1:
x = x[idx:]
parts = map(bold, x.split())
return ' '.join(parts)
def code(x):
return x
def env(x):
return italic(x)
def file(x):
return italic(x)
def parse_option_spec(spec=OPTIONS):
NORMAL, METADATA, HELP = 'NORMAL', 'METADATA', 'HELP'
state = NORMAL
@@ -192,7 +232,7 @@ def parse_option_spec(spec=OPTIONS):
current_cmd = None
for line in lines:
line = line.strip()
line = line.rstrip()
if state is NORMAL:
if not line:
continue
@@ -219,10 +259,11 @@ def parse_option_spec(spec=OPTIONS):
current_cmd['choices'] = {x.strip() for x in current_cmd['choices'].split(',')}
elif state is HELP:
if line:
current_cmd['help'] += ' ' + line.lstrip()
spc = '' if current_cmd['help'].endswith('\n') else ' '
current_cmd['help'] += spc + line
else:
if prev_line:
current_cmd['help'] += '\n\n\t'
current_cmd['help'] += '\n\n'
else:
state = NORMAL
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
@@ -235,17 +276,20 @@ def parse_option_spec(spec=OPTIONS):
def prettify(text):
role_map = globals()
def sub(m):
t = m.group(2)
for ch in m.group(1):
t = {'C': cyan, '_': italic, '*': bold, 'G': green, 'T': title}[ch](t)
return t
role, text = m.group(1, 2)
return role_map[role](text)
text = re.sub(r'[|]([a-zA-Z_*]+?) (.+?)[|]', sub, text)
text = re.sub(r':([a-z]+):`([^`]+)`', sub, text)
return text
def prettify_rst(text):
return re.sub(r':([a-z]+):`([^`]+)`(=[^\s.]+)', r':\1:`\2`:code:`\3`', text)
def version(add_rev=False):
rev = ''
from . import fast_data_types
@@ -287,6 +331,14 @@ def wrap(text, limit=80):
return reversed(lines)
default_msg = ('''\
Run the :italic:`{appname}` terminal emulator. You can also specify the :italic:`program`
to run inside :italic:`{appname}` as normal arguments following the :italic:`options`.
For example: {appname} /bin/sh
For comprehensive documentation for kitty, please see: https://sw.kovidgoyal.net/kitty''').format(appname=appname)
def print_help_for_seq(seq, usage, message, appname):
from kitty.utils import screen_size_function
screen_size = screen_size_function()
@@ -313,10 +365,7 @@ def print_help_for_seq(seq, usage, message, appname):
optstring = '[options] ' if seq else ''
a('{}: {} {}{}'.format(title('Usage'), bold(yellow(appname)), optstring, usage))
a('')
message = message or (
'Run the |G {appname}| terminal emulator. You can also specify the |_ program| to run inside |_ {appname}| as normal'
' arguments following the |_ options|. For example: {appname} /bin/sh'
).format(appname=appname)
message = message or default_msg
wa(prettify(message))
a('')
if seq:
@@ -354,6 +403,53 @@ def print_help_for_seq(seq, usage, message, appname):
print_help_for_seq.allow_pager = True
def seq_as_rst(seq, usage, message, appname, heading_char='-'):
import textwrap
blocks = []
a = blocks.append
usage = '[program-to-run ...]' if usage is None else usage
optstring = '[options] ' if seq else ''
a('.. highlight:: sh')
a('.. code-block:: sh')
a('')
a(' {} {}{}'.format(appname, optstring, usage))
a('')
message = message or default_msg
a(prettify_rst(message))
a('')
if seq:
a('Options')
a(heading_char * 30)
for opt in seq:
if isinstance(opt, str):
a(opt)
a('~' * (len(opt) + 10))
continue
help_text = opt['help']
if help_text == '!':
continue # hidden option
defn = '.. option:: '
if not opt.get('type', '').startswith('bool-'):
val_name = ' <{}>'.format(opt['dest'].upper())
else:
val_name = ''
a(defn + ', '.join(o + val_name for o in sorted(opt['aliases'])))
if opt.get('help'):
defval = opt.get('default')
t = help_text.replace('%default', str(defval)).strip()
a('')
a(textwrap.indent(prettify_rst(t), ' ' * 4))
if defval is not None:
a(textwrap.indent('Default: :code:`{}`'.format(defval), ' ' * 4))
if 'choices' in opt:
a(textwrap.indent('Choices: :code:`{}`'.format(', '.join(opt['choices'])), ' ' * 4))
a('')
text = '\n'.join(blocks)
return text
def defval_for_opt(opt):
dv = opt.get('default')
typ = opt.get('type', '')
@@ -489,6 +585,13 @@ def options_spec():
return options_spec.ans
def option_spec_as_rst(ospec=options_spec, usage=None, message=None, appname=None, heading_char='-'):
options = parse_option_spec(ospec())
seq, disabled = options
oc = Options(seq, usage, message, appname)
return seq_as_rst(oc.seq, oc.usage, oc.message, oc.appname, heading_char=heading_char)
def parse_args(args=None, ospec=options_spec, usage=None, message=None, appname=None):
options = parse_option_spec(ospec())
seq, disabled = options

View File

@@ -86,6 +86,10 @@ def screen_erase_in_line(how, private):
write(CSI + ('?' if private else '') + str(how) + 'K')
def screen_delete_lines(num):
write(CSI + str(num) + 'M')
def screen_cursor_up2(count):
write(CSI + '%dA' % count)

View File

@@ -10,7 +10,6 @@ from .cli import parse_args
from .config import parse_config, parse_send_text_bytes
from .constants import appname
from .tabs import SpecialWindow
from .utils import non_blocking_read
class MatchError(ValueError):
@@ -46,19 +45,19 @@ def cmd(short_desc, desc=None, options_spec=None, no_response=False, argspec='..
MATCH_WINDOW_OPTION = '''\
--match -m
The window to match. Match specifications are of the form:
|_ field:regexp|. Where field can be one of: id, title, pid, cwd, cmdline, num.
You can use the |_ ls| command to get a list of windows. Note that for
:italic:`field:regexp`. Where field can be one of: id, title, pid, cwd, cmdline, num.
You can use the :italic:`ls` command to get a list of windows. Note that for
numeric fields such as id, pid and num the expression is interpreted as a number,
not a regular expression. The field num refers to the window position in the current tab,
starting from zero and counting clockwise (this is the same as the order in which the
windows are reported by the |_ ls| command). The window id of the current window
windows are reported by the :italic:`ls` command). The window id of the current window
is available as the KITTY_WINDOW_ID environment variable.
'''
MATCH_TAB_OPTION = '''\
--match -m
The tab to match. Match specifications are of the form:
|_ field:regexp|. Where field can be one of: id, title, pid, cwd, cmdline.
You can use the |_ ls| command to get a list of tabs. Note that for
:italic:`field:regexp`. Where field can be one of: id, title, pid, cwd, cmdline.
You can use the :italic:`ls` command to get a list of tabs. Note that for
numeric fields such as id and pid the expression is interpreted as a number,
not a regular expression. When using title or id, first a matching tab is
looked for and if not found a matching window is looked for, and the tab
@@ -70,10 +69,10 @@ for that window is used.
@cmd(
'List all tabs/windows',
'List all windows. The list is returned as JSON tree. The top-level is a list of'
' operating system {appname} windows. Each OS window has an |_ id| and a list'
' of |_ tabs|. Each tab has its own |_ id|, a |_ title| and a list of |_ windows|.'
' Each window has an |_ id|, |_ title|, |_ current working directory|, |_ process id (PID)| and'
' |_ command-line| of the process running in the window.\n\n'
' operating system {appname} windows. Each OS window has an :italic:`id` and a list'
' of :italic:`tabs`. Each tab has its own :italic:`id`, a :italic:`title` and a list of :italic:`windows`.'
' Each window has an :italic:`id`, :italic:`title`, :italic:`current working directory`, :italic:`process id (PID)` and'
' :italic:`command-line` of the process running in the window.\n\n'
'You can use these criteria to select windows/tabs for the other commands.'.format(appname=appname),
argspec=''
)
@@ -110,14 +109,14 @@ def set_font_size(boss, window, payload):
@cmd(
'Send arbitrary text to specified windows',
'Send arbitrary text to specified windows. The text follows Python'
' escaping rules. So you can use escapes like |_ \\x1b| to send control codes'
' and |_ \\u21fa| to send unicode characters. If you use the |_ --match| option'
' escaping rules. So you can use escapes like :italic:`\\x1b` to send control codes'
' and :italic:`\\u21fa` to send unicode characters. If you use the :option:`kitty @ send-text --match` option'
' the text will be sent to all matched windows. By default, text is sent to'
' only the currently active window.',
options_spec=MATCH_WINDOW_OPTION + '''\n
--stdin
type=bool-set
Read the text to be sent from |_ stdin|. Note that in this case the text is sent as is,
Read the text to be sent from :italic:`stdin`. Note that in this case the text is sent as is,
not interpreted for escapes. If stdin is a terminal, you can press Ctrl-D to end reading.
@@ -132,27 +131,32 @@ def cmd_send_text(global_opts, opts, args):
limit = 1024
ret = {'match': opts.match, 'is_binary': False}
def pipe(src=sys.stdin):
def pipe():
ret['is_binary'] = True
import select
with non_blocking_read() as fd:
if sys.stdin.isatty():
import select
fd = sys.stdin.fileno()
keep_going = True
while keep_going:
rd = select.select([fd], [], [])
if rd:
data = sys.stdin.buffer.read()
if not data:
break
data = data.decode('utf-8')
if '\x04' in data:
data = data[:data.index('\x04')]
keep_going = False
while data:
ret['text'] = data[:limit]
yield ret
data = data[limit:]
else:
rd = select.select([fd], [], [])[0]
if not rd:
break
data = os.read(fd, limit)
if not data:
break # eof
data = data.decode('utf-8')
if '\x04' in data:
data = data[:data.index('\x04')]
keep_going = False
ret['text'] = data
yield ret
else:
while True:
data = sys.stdin.read(limit)
if not data:
break
ret['text'] = data[:limit]
yield ret
def chunks(text):
ret['is_binary'] = False
@@ -202,15 +206,21 @@ def send_text(boss, window, payload):
# set_window_title {{{
@cmd(
'Set the window title',
'Set the title for the specified window(s). If you use the |_ --match| option'
'Set the title for the specified window(s). If you use the :option:`kitty @ set-window-title --match` option'
' the title will be set for all matched windows. By default, only the window'
' in which the command is run is affected. If you do not specify a title, the'
' last title set by the child process running in the window will be used.',
options_spec=MATCH_WINDOW_OPTION,
options_spec='''
--temporary
type=bool-set
By default, if you use :italic:`set-window-title` the title will be permanently changed
and programs running in the window will not be able to change it again. If you
want to allow other programs to change it afterwards, use this option.
''' + '\n\n' + MATCH_WINDOW_OPTION,
argspec='TITLE ...'
)
def cmd_set_window_title(global_opts, opts, args):
return {'title': ' '.join(args), 'match': opts.match}
return {'title': ' '.join(args), 'match': opts.match, 'temporary': opts.temporary}
def set_window_title(boss, window, payload):
@@ -222,14 +232,19 @@ def set_window_title(boss, window, payload):
raise MatchError(match)
for window in windows:
if window:
window.set_title(payload['title'])
if payload['temporary']:
window.override_title = None
window.title_changed(payload['title'])
else:
window.set_title(payload['title'])
# }}}
# set_tab_title {{{
@cmd(
'Set the tab title',
'Set the title for the specified tab(s). If you use the |_ --match| option'
'Set the title for the specified tab(s). If you use the :option:`kitty @ set-tab-title --match` option'
' the title will be set for all matched tabs. By default, only the tab'
' in which the command is run is affected. If you do not specify a title, the'
' title of the currently active window in the tab is used.',
@@ -297,8 +312,8 @@ The number of cells to change the size by, can be negative to decrease the size.
type=choices
choices=horizontal,vertical,reset
default=horizontal
The axis along which to resize. If |_ horizontal|, it will make the window wider or narrower by the specified increment.
If |_ vertical|, it will make the window taller or shorter by the specified increment. The special value |_ reset| will
The axis along which to resize. If :italic:`horizontal`, it will make the window wider or narrower by the specified increment.
If :italic:`vertical`, it will make the window taller or shorter by the specified increment. The special value :italic:`reset` will
reset the layout to its default configuration.
@@ -363,12 +378,12 @@ def close_tab(boss, window, payload):
# new_window {{{
@cmd(
'Open new window',
'Open a new window in the specified tab. If you use the |_ --match| option'
'Open a new window in the specified tab. If you use the :option:`kitty @ new-window --match` option'
' the first matching tab is used. Otherwise the currently active tab is used.'
' Prints out the id of the newly opened window. Any command line arguments'
' are assumed to be the command line used to run in the new window, if none'
' are provided, the default shell is run. For example:\n'
'|_ kitty @ new-window --title Email mutt|',
':italic:`kitty @ new-window --title Email mutt`',
options_spec=MATCH_TAB_OPTION + '''\n
--title
The title for the new window. By default it will use the title set by the
@@ -596,7 +611,7 @@ def set_colors(boss, window, payload):
@cmd(
'Set the background_opacity',
'Set the background opacity for the specified windows. This will only work if you have turned on'
' dynamic_background_opacity in kitty.conf. The background opacity affects all kitty windows in a'
' :opt:`dynamic_background_opacity` in :file:`kitty.conf`. The background opacity affects all kitty windows in a'
' single os_window. For example: kitty @ set-background-opacity 0.5',
options_spec='''\
--all -a
@@ -635,8 +650,12 @@ def set_background_opacity(boss, window, payload):
cmap = {v.name: v for v in globals().values() if hasattr(v, 'is_cmd')}
def cli_params_for(func):
return (func.options_spec or '\n').format, func.argspec, func.desc, '{} @ {}'.format(appname, func.name)
def parse_subcommand_cli(func, args):
opts, items = parse_args(args[1:], (func.options_spec or '\n').format, func.argspec, func.desc, '{} @ {}'.format(appname, func.name))
opts, items = parse_args(args[1:], *cli_params_for(func))
if func.args_count is not None and func.args_count != len(items):
if func.args_count == 0:
raise SystemExit('Unknown extra argument(s) supplied to {}'.format(func.name))

View File

@@ -65,9 +65,15 @@ find_app_name(void) {
- (void) show_preferences : (id)sender {
(void)sender;
call_boss(edit_config_file, NULL);
set_cocoa_pending_action(PREFERENCES_WINDOW);
}
- (void) new_os_window : (id)sender {
(void)sender;
set_cocoa_pending_action(NEW_OS_WINDOW);
}
+ (GlobalMenuTarget *) shared_instance
{
static GlobalMenuTarget *sharedGlobalMenuTarget = nil;
@@ -81,6 +87,20 @@ find_app_name(void) {
@end
static unichar new_window_key = 0;
static NSEventModifierFlags new_window_mods = 0;
static PyObject*
cocoa_set_new_window_trigger(PyObject *self UNUSED, PyObject *args) {
int mods, key;
if (!PyArg_ParseTuple(args, "ii", &mods, &key)) return NULL;
int nwm;
get_cocoa_key_equivalent(key, mods, &new_window_key, &nwm);
new_window_mods = nwm;
if (new_window_key) Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
void
cocoa_create_global_menu(void) {
NSString* app_name = find_app_name();
@@ -97,9 +117,19 @@ cocoa_create_global_menu(void) {
action:@selector(orderFrontStandardAboutPanel:)
keyEquivalent:@""];
[appMenu addItem:[NSMenuItem separatorItem]];
NSMenuItem* preferences_menu_item = [[NSMenuItem alloc] initWithTitle:@"Preferences..." action:@selector(show_preferences:) keyEquivalent:@","];
NSMenuItem* preferences_menu_item = [[NSMenuItem alloc] initWithTitle:@"Preferences..." action:@selector(show_preferences:) keyEquivalent:@","], *new_os_window_menu_item = NULL;
[preferences_menu_item setTarget:global_menu_target];
[appMenu addItem:preferences_menu_item];
if (new_window_key) {
NSString *s = [NSString stringWithCharacters:&new_window_key length:1];
new_os_window_menu_item = [[NSMenuItem alloc] initWithTitle:@"New OS window" action:@selector(new_os_window:) keyEquivalent:s];
[new_os_window_menu_item setKeyEquivalentModifierMask:new_window_mods];
[new_os_window_menu_item setTarget:global_menu_target];
[appMenu addItem:new_os_window_menu_item];
[s release];
}
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", app_name]
action:@selector(hide:)
keyEquivalent:@"h"];
@@ -150,6 +180,9 @@ cocoa_create_global_menu(void) {
[NSApp setWindowsMenu:windowMenu];
[windowMenu release];
[preferences_menu_item release];
if (new_os_window_menu_item) {
[new_os_window_menu_item release];
}
[bar release];
}
@@ -239,6 +272,7 @@ cocoa_set_titlebar_color(void *w, color_type titlebar_color)
static PyMethodDef module_methods[] = {
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
{"cwd_of_process", (PyCFunction)cwd_of_process, METH_O, ""},
{"cocoa_set_new_window_trigger", (PyCFunction)cocoa_set_new_window_trigger, METH_VARARGS, ""},
{NULL, NULL, 0, NULL} /* Sentinel */
};

View File

@@ -108,8 +108,8 @@ patch_color_profiles(PyObject *module UNUSED, PyObject *args) {
v = PyDict_GetItemString(spec, key);
if (v) {
color_type color = PyLong_AsUnsignedLong(v);
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(profiles); i++) {
self = (ColorProfile*)PyTuple_GET_ITEM(profiles, i);
for (Py_ssize_t j = 0; j < PyTuple_GET_SIZE(profiles); j++) {
self = (ColorProfile*)PyTuple_GET_ITEM(profiles, j);
self->color_table[i] = color;
if (change_configured) self->orig_color_table[i] = color;
self->dirty = true;
@@ -220,6 +220,21 @@ copy_color_table_to_buffer(ColorProfile *self, color_type *buf, int offset, size
self->dirty = false;
}
void
colorprofile_push_dynamic_colors(ColorProfile *self) {
if (self->dynamic_color_stack_idx >= arraysz(self->dynamic_color_stack)) {
memmove(self->dynamic_color_stack, self->dynamic_color_stack + 1, sizeof(self->dynamic_color_stack) - sizeof(self->dynamic_color_stack[0]));
self->dynamic_color_stack_idx = arraysz(self->dynamic_color_stack) - 1;
}
self->dynamic_color_stack[self->dynamic_color_stack_idx++] = self->overridden;
}
void
colorprofile_pop_dynamic_colors(ColorProfile *self) {
if (!self->dynamic_color_stack_idx) return;
self->overridden = self->dynamic_color_stack[--(self->dynamic_color_stack_idx)];
}
static PyObject*
color_table_address(ColorProfile *self, PyObject *a UNUSED) {
#define color_table_address_doc "Pointer address to start of color table"

0
kitty/conf/__init__.py Normal file
View File

268
kitty/conf/definition.py Normal file
View File

@@ -0,0 +1,268 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import re
from functools import partial
from .utils import to_bool
def to_string(x):
return x
class Group:
__slots__ = 'name', 'short_text', 'start_text', 'end_text'
def __init__(self, name, short_text, start_text='', end_text=''):
self.name, self.short_text = name, short_text.strip()
self.start_text, self.end_text = start_text.strip(), end_text.strip()
class Option:
__slots__ = 'name', 'group', 'long_text', 'option_type', 'defval_as_string', 'add_to_default', 'add_to_docs', 'line'
def __init__(self, name, group, defval, option_type, long_text, add_to_default, add_to_docs):
self.name, self.group = name, group
self.long_text, self.option_type = long_text.strip(), option_type
self.defval_as_string = defval
self.add_to_default = add_to_default
self.add_to_docs = add_to_docs
self.line = self.name + ' ' + self.defval_as_string
class Shortcut:
__slots__ = 'name', 'group', 'key', 'action_def', 'short_text', 'long_text', 'add_to_default', 'add_to_docs', 'line'
def __init__(self, name, group, key, action_def, short_text, long_text, add_to_default, add_to_docs):
self.name, self.group, self.key, self.action_def = name, group, key, action_def
self.short_text, self.long_text = short_text, long_text
self.add_to_default = add_to_default
self.add_to_docs = add_to_docs
self.line = 'map ' + self.key + ' ' + self.action_def
def option(
all_options,
group,
name,
defval,
long_text='',
option_type=to_string,
add_to_default=True,
add_to_docs=True
):
is_multiple = name.startswith('+')
if is_multiple:
name = name[1:]
defval_type = type(defval)
if defval_type is not str:
if option_type is to_string:
if defval_type is bool:
option_type = to_bool
else:
option_type = defval_type
if defval_type is bool:
defval = 'yes' if defval else 'no'
else:
defval = str(defval)
key = name
if is_multiple:
key = name + ' ' + defval.partition(' ')[0]
ans = Option(name, group[0], defval, option_type, long_text, add_to_default, add_to_docs)
all_options[key] = ans
return ans
def shortcut(
all_options,
group,
action_name,
key,
action_def,
short_text='',
long_text='',
add_to_default=True,
add_to_docs=True
):
ans = Shortcut(action_name, group[0], key, action_def, short_text, long_text, add_to_default, add_to_docs)
key = 'sc-' + action_name
all_options.setdefault(key, []).append(ans)
return ans
def option_func(all_options, all_groups):
all_groups = {k: Group(k, *v) for k, v in all_groups.items()}
group = [None]
def change_group(name):
group[0] = all_groups[name]
return partial(option, all_options, group), partial(shortcut, all_options, group), change_group, all_groups
def merged_opts(all_options, opt, i):
yield opt
for k in range(i + 1, len(all_options)):
q = all_options[k]
if not isinstance(q, Option):
break
if not q.long_text and q.add_to_docs:
yield q
else:
break
def remove_markup(text):
return re.sub(r':([a-zA-Z0-9]+):`(.+?)`', r'\2', text, flags=re.DOTALL)
def iter_blocks(lines):
current_block = []
prev_indent = 0
for line in lines:
indent_size = len(line) - len(line.lstrip())
if indent_size != prev_indent or not line:
if current_block:
yield current_block, prev_indent
current_block = []
prev_indent = indent_size
if not line:
yield [''], 100
else:
current_block.append(line)
if current_block:
yield current_block, indent_size
def wrapped_block(lines):
wrapper = getattr(wrapped_block, 'wrapper', None)
if wrapper is None:
import textwrap
wrapper = wrapped_block.wrapper = textwrap.TextWrapper(
initial_indent='#: ', subsequent_indent='#: ', width=70, break_long_words=False
)
for block, indent_size in iter_blocks(lines):
if indent_size > 0:
for line in block:
if not line:
yield line
else:
yield '#: ' + line
else:
for line in wrapper.wrap('\n'.join(block)):
yield line
def render_block(text):
text = remove_markup(text)
lines = text.splitlines()
return '\n'.join(wrapped_block(lines))
def as_conf_file(all_options):
ans = ['# vim:fileencoding=utf-8:ft=conf:foldmethod=marker', '']
a = ans.append
current_group = None
num_open_folds = 0
all_options = list(all_options)
def render_group(group, is_shortcut):
nonlocal num_open_folds
if is_shortcut or '.' not in group.name:
a('#: ' + group.short_text + ' {{''{')
num_open_folds += 1
a('')
if group.start_text:
a(render_block(group.start_text))
a('')
def handle_group_end(group, new_group_name='', new_group_is_shortcut=False):
nonlocal num_open_folds
if group.end_text:
a(''), a(render_block(group.end_text))
is_subgroup = new_group_name.startswith(group.name + '.')
if not is_subgroup and num_open_folds > 0:
a('#: }}''}'), a('')
num_open_folds -= 1
def handle_group(new_group, is_shortcut=False):
nonlocal current_group
if new_group is not current_group:
if current_group:
handle_group_end(current_group, new_group.name, is_shortcut)
current_group = new_group
render_group(current_group, is_shortcut)
def handle_shortcut(shortcuts):
handle_group(shortcuts[0].group, True)
for sc in shortcuts:
if sc.add_to_default:
a('map {} {}'.format(sc.key, sc.action_def))
if sc.long_text:
a(''), a(render_block(sc.long_text.strip())), a('')
def handle_option(opt):
if not opt.long_text or not opt.add_to_docs:
return
handle_group(opt.group)
mopts = list(merged_opts(all_options, opt, i))
sz = max(len(x.name) for x in mopts)
for mo in mopts:
prefix = '' if mo.add_to_default else '# '
a('{}{} {}'.format(prefix, mo.name.ljust(sz), mo.defval_as_string))
a('')
a(render_block(opt.long_text))
a('')
for i, opt in enumerate(all_options):
if isinstance(opt, Option):
handle_option(opt)
else:
handle_shortcut(opt)
if current_group:
handle_group_end(current_group)
while num_open_folds > 0:
a('# }}''}')
num_open_folds -= 1
map_groups = []
start = count = None
for i, line in enumerate(ans):
if line.startswith('map '):
if start is None:
start = i
count = 1
else:
count += 1
else:
if start is not None:
map_groups.append((start, count))
start = None
for start, count in map_groups:
r = range(start, start + count)
sz = max(len(ans[i].split(' ', 3)[1]) for i in r)
for i in r:
line = ans[i]
parts = line.split(' ', 3)
parts[1] = parts[1].ljust(sz)
ans[i] = ' '.join(parts)
return ans
def config_lines(all_options):
for opt in all_options.values():
if isinstance(opt, Option):
if opt.add_to_default:
yield opt.line
else:
for sc in opt:
if sc.add_to_default:
yield sc.line

View File

@@ -7,8 +7,8 @@ import os
import re
import shlex
from .rgb import to_color as as_color
from .utils import log_error
from ..rgb import to_color as as_color
from ..utils import log_error
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
@@ -43,6 +43,18 @@ def python_string(text):
return ast.literal_eval("'''" + text.replace("'''", "'\\''") + "'''")
def choices(*choices):
defval = choices[0]
choices = frozenset(choices)
def choice(x):
x = x.lower()
if x not in choices:
x = defval
return x
return choice
def parse_line(line, type_map, special_handling, ans, all_keys, base_path_for_includes):
line = line.strip()
if not line or line.startswith('#'):
@@ -78,7 +90,7 @@ def _parse(lines, type_map, special_handling, ans, all_keys):
if name:
base_path_for_includes = os.path.dirname(os.path.abspath(name))
else:
from .constants import config_dir
from ..constants import config_dir
base_path_for_includes = config_dir
for line in lines:
parse_line(line, type_map, special_handling, ans, all_keys, base_path_for_includes)
@@ -164,9 +176,8 @@ def load_config(Options, defaults, parse_config, merge_configs, *paths, override
return Options(ans)
def init_config(defaults_path, parse_config):
with open(defaults_path, encoding='utf-8', errors='replace') as f:
defaults = parse_config(f, check_keys=False)
def init_config(default_config_lines, parse_config):
defaults = parse_config(default_config_lines, check_keys=False)
Options = create_options_class(defaults.keys())
defaults = Options(defaults)
return Options, defaults
@@ -188,7 +199,7 @@ def key_func():
def parse_kittens_shortcut(sc):
from kitty.key_encoding import config_key_map, config_mod_map, text_match
from ..key_encoding import config_key_map, config_mod_map, text_match
if sc.endswith('+'):
parts = list(filter(None, sc.rstrip('+').split('+') + ['+']))
else:

View File

@@ -10,61 +10,15 @@ from collections import namedtuple
from contextlib import contextmanager
from . import fast_data_types as defines
from .config_utils import (
from .conf.definition import as_conf_file, config_lines
from .conf.utils import (
init_config, key_func, load_config as _load_config, merge_dicts,
parse_config_base, positive_float, positive_int, python_string, to_bool,
to_cmdline, to_color, unit_float
parse_config_base, python_string, to_bool, to_cmdline
)
from .config_data import all_options, parse_mods, type_map
from .constants import cache_dir, defconf
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
from .layout import all_layouts
from .rgb import color_as_int, color_from_int
from .utils import log_error
MINIMUM_FONT_SIZE = 4
def to_font_size(x):
return max(MINIMUM_FONT_SIZE, float(x))
cshapes = {
'block': CURSOR_BLOCK,
'beam': CURSOR_BEAM,
'underline': CURSOR_UNDERLINE
}
def to_cursor_shape(x):
try:
return cshapes[x.lower()]
except KeyError:
raise ValueError(
'Invalid cursor shape: {} allowed values are {}'.format(
x, ', '.join(cshapes)
)
)
mod_map = {'CTRL': 'CONTROL', 'CMD': 'SUPER', '': 'SUPER', '': 'ALT', 'OPTION': 'ALT', 'KITTY_MOD': 'KITTY'}
def parse_mods(parts, sc):
def map_mod(m):
return mod_map.get(m, m)
mods = 0
for m in parts:
try:
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
except AttributeError:
log_error('Shortcut: {} has unknown modifier, ignoring'.format(sc))
return
return mods
named_keys = {
"'": 'APOSTROPHE',
',': 'COMMA',
@@ -149,6 +103,22 @@ def float_parse(func, rest):
return func, (float(rest),)
@func_with_args('change_font_size')
def parse_change_font_size(func, rest):
vals = rest.split(' ', 1)
if len(vals) != 2:
log_error('Invalid change_font_size specification: {}, treating it as default'.format(rest))
args = [True, None, 0]
else:
args = [vals[0].lower() == 'all', None, 0]
amt = vals[1]
if amt[0] in '+-':
args[1] = amt[0]
amt = amt[1:]
args[2] = float(amt)
return func, args
def parse_key_action(action):
parts = action.split(' ', 1)
func = parts[0]
@@ -273,149 +243,6 @@ def parse_send_text(val, key_definitions):
return parse_key(key_str, key_definitions)
def to_modifiers(val):
return parse_mods(val.split('+'), val) or 0
def uniq(vals, result_type=list):
seen = set()
seen_add = seen.add
return result_type(x for x in vals if x not in seen and not seen_add(x))
def to_layout_names(raw):
parts = [x.strip().lower() for x in raw.split(',')]
ans = []
for p in parts:
if p == '*':
ans.extend(sorted(all_layouts))
continue
name = p.partition(':')[0]
if name not in all_layouts:
raise ValueError('The window layout {} is unknown'.format(p))
ans.append(p)
return uniq(ans)
def adjust_line_height(x):
if x.endswith('%'):
return float(x[:-1].strip()) / 100.0
return int(x)
def macos_titlebar_color(x):
x = x.strip('"')
if x == 'system':
return 0
if x == 'background':
return 1
return (color_as_int(to_color(x)) << 8) | 2
def box_drawing_scale(x):
ans = tuple(float(x.strip()) for x in x.split(','))
if len(ans) != 4:
raise ValueError('Invalid box_drawing scale, must have four entries')
return ans
def tab_separator(x):
for q in '\'"':
if x.startswith(q) and x.endswith(q):
x = x[1:-1]
break
if not x.strip():
x = ('\xa0' * len(x)) if x else defaults.tab_separator
return x
def tab_font_style(x):
return {
'bold-italic': (True, True),
'bold': (True, False),
'italic': (False, True)
}.get(x.lower().replace('_', '-'), (False, False))
def tab_bar_edge(x):
return {'top': 1, 'bottom': 3}.get(x.lower(), 3)
def url_style(x):
return url_style.map.get(x, url_style.map['curly'])
url_style.map = dict(
((v, i) for i, v in enumerate('none single double curly'.split()))
)
type_map = {
'allow_remote_control': to_bool,
'adjust_line_height': adjust_line_height,
'adjust_column_width': adjust_line_height,
'scrollback_lines': positive_int,
'scrollback_pager': to_cmdline,
'open_url_with': to_cmdline,
'font_size': to_font_size,
'font_size_delta': positive_float,
'focus_follows_mouse': to_bool,
'cursor_shape': to_cursor_shape,
'open_url_modifiers': to_modifiers,
'rectangle_select_modifiers': to_modifiers,
'repaint_delay': positive_int,
'input_delay': positive_int,
'sync_to_monitor': to_bool,
'close_on_child_death': to_bool,
'window_border_width': positive_float,
'window_margin_width': positive_float,
'window_padding_width': positive_float,
'wheel_scroll_multiplier': float,
'visual_bell_duration': positive_float,
'enable_audio_bell': to_bool,
'click_interval': positive_float,
'mouse_hide_wait': positive_float,
'cursor_blink_interval': positive_float,
'cursor_stop_blinking_after': positive_float,
'enabled_layouts': to_layout_names,
'remember_window_size': to_bool,
'initial_window_width': positive_int,
'initial_window_height': positive_int,
'macos_hide_titlebar': to_bool,
'macos_hide_from_tasks': to_bool,
'macos_option_as_alt': to_bool,
'macos_titlebar_color': macos_titlebar_color,
'box_drawing_scale': box_drawing_scale,
'dynamic_background_opacity': to_bool,
'background_opacity': unit_float,
'dim_opacity': unit_float,
'tab_separator': tab_separator,
'active_tab_font_style': tab_font_style,
'inactive_tab_font_style': tab_font_style,
'inactive_text_alpha': unit_float,
'url_style': url_style,
'copy_on_select': to_bool,
'window_alert_on_bell': to_bool,
'tab_bar_edge': tab_bar_edge,
'bell_on_tab': to_bool,
'kitty_mod': to_modifiers,
'clear_all_shortcuts': to_bool,
'clipboard_control': lambda x: frozenset(x.lower().split()),
'window_resize_step_cells': int,
'window_resize_step_lines': int,
}
for name in (
'foreground background cursor active_border_color inactive_border_color'
' selection_foreground selection_background url_color bell_border_color'
).split():
type_map[name] = to_color
for i in range(256):
type_map['color{}'.format(i)] = to_color
for a in ('active', 'inactive'):
for b in ('foreground', 'background'):
type_map['%s_tab_%s' % (a, b)] = to_color
def special_handling(key, val, ans):
if key == 'map':
parse_key(val, ans['key_definitions'])
@@ -434,9 +261,6 @@ def special_handling(key, val, ans):
defaults = None
default_config_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)), 'kitty.conf'
)
def parse_config(lines, check_keys=True):
@@ -454,15 +278,10 @@ def parse_config(lines, check_keys=True):
def parse_defaults(lines, check_keys=False):
ans = parse_config(lines, check_keys)
dfctl = defines.default_color_table()
for i in range(16, 256):
k = 'color{}'.format(i)
ans.setdefault(k, color_from_int(dfctl[i]))
return ans
Options, defaults = init_config(default_config_path, parse_defaults)
Options, defaults = init_config(config_lines(all_options), parse_defaults)
actions = frozenset(all_key_actions) | frozenset(
'run_simple_kitten combine send_text goto_tab goto_layout set_font_size new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'.
split()
@@ -534,27 +353,43 @@ def cached_values_for(name):
err))
def initial_window_size(opts, cached_values):
w, h = opts.initial_window_width, opts.initial_window_height
def initial_window_size_func(opts, cached_values):
if 'window-size' in cached_values and opts.remember_window_size:
ws = cached_values['window-size']
try:
w, h = map(int, ws)
def initial_window_size(*a):
return w, h
return initial_window_size
except Exception:
log_error('Invalid cached window size, ignoring')
return w, h
w, w_unit = opts.initial_window_width
h, h_unit = opts.initial_window_height
def get_window_size(cell_width, cell_height, dpi_x, dpi_y):
if w_unit == 'cells':
width = cell_width * w + (dpi_x / 72) * (opts.window_margin_width + opts.window_padding_width) + 1
else:
width = w
if h_unit == 'cells':
height = cell_height * h + (dpi_y / 72) * (opts.window_margin_width + opts.window_padding_width) + 1
else:
height = h
return width, height
return get_window_size
def commented_out_default_config():
with open(default_config_path, encoding='utf-8', errors='replace') as f:
config = f.read()
lines = []
for line in config.splitlines():
if line.strip() and not line.startswith('#'):
ans = []
for line in as_conf_file(all_options.values()):
if line and line[0] != '#':
line = '# ' + line
lines.append(line)
config = '\n'.join(lines)
return config
ans.append(line)
return '\n'.join(ans)
def prepare_config_file_for_editing():
@@ -564,7 +399,7 @@ def prepare_config_file_for_editing():
os.makedirs(d)
except FileExistsError:
pass
with open(defconf, 'w') as f:
with open(defconf, 'w', encoding='utf-8') as f:
f.write(commented_out_default_config())
return defconf

877
kitty/config_data.py Normal file
View File

@@ -0,0 +1,877 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
# Utils {{{
import os
from gettext import gettext as _
from . import fast_data_types as defines
from .conf.definition import option_func
from .conf.utils import (
choices, positive_float, positive_int, to_cmdline, to_color, unit_float
)
from .constants import config_dir, is_macos
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
from .layout import all_layouts
from .rgb import color_as_int, color_as_sharp, color_from_int
from .utils import log_error
MINIMUM_FONT_SIZE = 4
mod_map = {'CTRL': 'CONTROL', 'CMD': 'SUPER', '': 'SUPER',
'': 'ALT', 'OPTION': 'ALT', 'KITTY_MOD': 'KITTY'}
def parse_mods(parts, sc):
def map_mod(m):
return mod_map.get(m, m)
mods = 0
for m in parts:
try:
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
except AttributeError:
log_error('Shortcut: {} has unknown modifier, ignoring'.format(sc))
return
return mods
def to_modifiers(val):
return parse_mods(val.split('+'), val) or 0
def uniq(vals, result_type=list):
seen = set()
seen_add = seen.add
return result_type(x for x in vals if x not in seen and not seen_add(x))
# }}}
# Groups {{{
all_options = {}
o, k, g, all_groups = option_func(all_options, {
'fonts': [
_('Fonts'),
_('kitty has very powerful font management. You can configure individual\n'
'font faces and even specify special fonts for particular characters.')
],
'cursor': [_('Cursor customization'), ],
'scrollback': [_('Scrollback'), ],
'mouse': [_('Mouse'), ],
'performance': [_('Performance tuning')],
'bell': [_('Terminal bell')],
'window': [_('Window layout')],
'tabbar': [_('Tab bar')],
'colors': [_('Color scheme')],
'colors.table': [
_('The color table'),
_('''\
The 16 terminal colors. There are 8 basic colors, each color has a dull and
bright version. You can also set the remaining colors from the 256 color table
as color16 to color255.''')
],
'advanced': [_('Advanced')],
'os': [_('OS specific tweaks')],
'shortcuts': [
_('Keyboard shortcuts'),
_('''\
For a list of key names, see: :link:`GLFW keys <http://www.glfw.org/docs/latest/group__keys.html>`
For a list of modifier names, see: :link:`GLFW mods <http://www.glfw.org/docs/latest/group__mods.html>`
You can use the special action :code:`no_op` to unmap a keyboard shortcut that is
assigned in the default configuration.
You can combine multiple actions to be triggered by a single shortcut, using the
syntax below::
map key combine <separator> action1 <separator> action2 <separator> action3 ...
For example::
map kitty_mod+e combine : new_window : next_layout
this will create a new window and switch to the next available layout
You can use multi-key shortcuts using the syntax shown below::
map key1>key2>key3 action
For example::
map ctrl+f>2 set_font_size 20
''')
],
'shortcuts.clipboard': [_('Clipboard')],
'shortcuts.scrolling': [_('Scrolling')],
'shortcuts.misc': [_('Miscellaneous')],
'shortcuts.window': [_('Window management')],
'shortcuts.tab': [
_('Tab management'), '',
_('''\
You can also create shortcuts to go to specific tabs, with 1 being the first tab::
map ctrl+alt+1 goto_tab 1
map ctrl+alt+2 goto_tab 2
Just as with :code:`new_window` above, you can also pass the name of arbitrary
commands to run when using new_tab and use :code:`new_tab_with_cwd`.
''')],
'shortcuts.layout': [
_('Layout management'), '',
_('''\
You can also create shortcuts to switch to specific layouts::
map ctrl+alt+t goto_layout tall
map ctrl+alt+s goto_layout stack
''')],
'shortcuts.fonts': [
_('Font sizes'), _('''\
You can change the font size for all top-level kitty windows at a time
or only the current one.
'''), _('''\
To setup shortcuts for specific font sizes::
map kitty_mod+f6 change_font_size all 10.0
To setup shortcuts to change only the current window's font size::
map kitty_mod+f6 change_font_size current 10.0
''')],
'shortcuts.selection': [
_('Select and act on visible text'), _('''\
Use the hints kitten to select text and either pass it to an external program or
insert it into the terminal or copy it to the clipboard.
'''), _('''
The hints kitten has many more modes of operation that you can map to different
shortcuts. For a full description see :doc:`kittens/hints`.''')],
})
# }}}
g('fonts') # {{{
o(
'font_family',
'monospace',
long_text=_('''
You can specify different fonts for the bold/italic/bold-italic variants.
By default they are derived automatically, by the OSes font system. Setting
them manually is useful for font families that have many weight variants like
Book, Medium, Thick, etc. For example::
font_family Operator Mono Book
bold_font Operator Mono Medium
italic_font Operator Mono Book Italic
bold_italic_font Operator Mono Medium Italic
''')
)
o('bold_font', 'auto')
o('italic_font', 'auto')
o('bold_italic_font', 'auto')
def to_font_size(x):
return max(MINIMUM_FONT_SIZE, float(x))
o('font_size', 11.0, long_text=_('Font size (in pts)'), option_type=to_font_size)
def adjust_line_height(x):
if x.endswith('%'):
return float(x[:-1].strip()) / 100.0
return int(x)
o('adjust_line_height', 0, option_type=adjust_line_height, long_text=_('''
Change the size of each character cell kitty renders. You can use either numbers,
which are interpreted as pixels or percentages (number followed by %), which
are interpreted as percentages of the unmodified values. You can use negative
pixels or percentages less than 100% to reduce sizes (but this might cause
rendering artifacts).'''))
o('adjust_column_width', 0, option_type=adjust_line_height)
o(
'+symbol_map',
'U+E0A0-U+E0A2,U+E0B0-U+E0B3 PowerlineSymbols',
add_to_default=False,
long_text=_('''
Map the specified unicode codepoints to a particular font. Useful if you need
special rendering for some symbols, such as for Powerline. Avoids the need for
patched fonts. Each unicode code point is specified in the form :code:`U+<code point
in hexadecimal>`. You can specify multiple code points, separated by commas and
ranges separated by hyphens. :code:`symbol_map` itself can be specified multiple times.
Syntax is::
symbol_map codepoints Font Family Name
'''))
def box_drawing_scale(x):
ans = tuple(float(x.strip()) for x in x.split(','))
if len(ans) != 4:
raise ValueError('Invalid box_drawing scale, must have four entries')
return ans
o(
'box_drawing_scale',
'0.001, 1, 1.5, 2',
option_type=box_drawing_scale,
long_text=_('''
Change the sizes of the lines used for the box drawing unicode characters
These values are in pts. They will be scaled by the monitor DPI to arrive at
a pixel value. There must be four values corresponding to thin, normal, thick,
and very thick lines.
'''))
# }}}
g('cursor') # {{{
cshapes = {
'block': CURSOR_BLOCK,
'beam': CURSOR_BEAM,
'underline': CURSOR_UNDERLINE
}
def to_cursor_shape(x):
try:
return cshapes[x.lower()]
except KeyError:
raise ValueError(
'Invalid cursor shape: {} allowed values are {}'.format(
x, ', '.join(cshapes)
)
)
o('cursor', '#cccccc', _('Default cursor color'), option_type=to_color)
o('cursor_shape', 'block', option_type=to_cursor_shape, long_text=_(
'The cursor shape can be one of (block, beam, underline)'))
o('cursor_blink_interval', 0.5, option_type=positive_float, long_text=_('''
The interval (in seconds) at which to blink the cursor. Set to zero to disable
blinking. Note that numbers smaller than :opt:`repaint_delay` will be limited
to :opt:`repaint_delay`. Stop blinking cursor after the specified number of
seconds of keyboard inactivity. Set to zero to never stop blinking.
'''))
o('cursor_stop_blinking_after', 15.0, option_type=positive_float)
# }}}
g('scrollback') # {{{
o('scrollback_lines', 2000, option_type=positive_int, long_text=_('''
Number of lines of history to keep in memory for scrolling back. Memory is allocated
on demand.'''))
o('scrollback_pager', 'less --chop-long-lines --RAW-CONTROL-CHARS +INPUT_LINE_NUMBER', option_type=to_cmdline, long_text=_('''
Program with which to view scrollback in a new window. The scrollback buffer is
passed as STDIN to this program. If you change it, make sure the program you
use can handle ANSI escape sequences for colors and text formatting.
INPUT_LINE_NUMBER in the command line above will be replaced by an integer
representing which line should be at the top of the screen.'''))
o('wheel_scroll_multiplier', 5.0, long_text=_('''
Modify the amount scrolled by the mouse wheel or touchpad. Use
negative numbers to change scroll direction.'''))
# }}}
g('mouse') # {{{
o('url_color', '#0087BD', option_type=to_color, long_text=_('''
The color and style for highlighting URLs on mouse-over.
:code:`url_style` can be one of: none, single, double, curly'''))
def url_style(x):
return url_style.map.get(x, url_style.map['curly'])
url_style.map = dict(
((v, i) for i, v in enumerate('none single double curly'.split()))
)
o('url_style', 'curly', option_type=url_style)
o('open_url_modifiers', 'kitty_mod', option_type=to_modifiers, long_text=_('''
The modifier keys to press when clicking with the
mouse on URLs to open the URL'''))
o('open_url_with', 'default', option_type=to_cmdline, long_text=_('''
The program with which to open URLs that are clicked on.
The special value :code:`default` means to use the
operating system's default URL handler.'''))
o('copy_on_select', False, long_text=_('''
Copy to clipboard on select. With this enabled, simply selecting text with
the mouse will cause the text to be copied to clipboard. Useful on platforms
such as macOS/Wayland that do not have the concept of primary selections. Note
that this is a security risk, as all programs, including websites open in your
browser can read the contents of the clipboard.'''))
o('rectangle_select_modifiers', 'ctrl+alt', option_type=to_modifiers, long_text=_('''
The modifiers to use rectangular selection (i.e. to select text in a
rectangular block with the mouse)'''))
o('select_by_word_characters', ':@-./_~?&=%+#', long_text=_('''
Characters considered part of a word when double clicking. In addition to these characters
any character that is marked as an alpha-numeric character in the unicode
database will be matched.'''))
o('click_interval', 0.5, option_type=positive_float, long_text=_('''
The interval between successive clicks to detect
double/triple clicks (in seconds)'''))
o('mouse_hide_wait', 3.0, option_type=positive_float, long_text=_('''
Hide mouse cursor after the specified number of seconds
of the mouse not being used. Set to zero to disable mouse cursor hiding.'''))
o('focus_follows_mouse', False, long_text=_('''
Set the active window to the window under the mouse when
moving the mouse around'''))
# }}}
g('performance') # {{{
o('repaint_delay', 10, option_type=positive_int, long_text=_('''
Delay (in milliseconds) between screen updates. Decreasing it, increases
frames-per-second (FPS) at the cost of more CPU usage. The default value
yields ~100 FPS which is more than sufficient for most uses. Note that to
actually achieve 100 FPS you have to either set :opt:`sync_to_monitor` to no
or use a monitor with a high refresh rate.'''))
o('input_delay', 3, option_type=positive_int, long_text=_('''
Delay (in milliseconds) before input from the program running in the terminal
is processed. Note that decreasing it will increase responsiveness, but also
increase CPU usage and might cause flicker in full screen programs that
redraw the entire screen on each loop, because kitty is so fast that partial
screen updates will be drawn.'''))
o('sync_to_monitor', True, long_text=_('''
Sync screen updates to the refresh rate of the monitor. This prevents
tearing (https://en.wikipedia.org/wiki/Screen_tearing) when scrolling. However,
it limits the rendering speed to the refresh rate of your monitor. With a
very high speed mouse/high keyboard repeat rate, you may notice some slight input latency.
If so, set this to no.'''))
# }}}
g('bell') # {{{
o('enable_audio_bell', True, long_text=_('''
Enable/disable the audio bell. Useful in environments that require silence.'''))
o('visual_bell_duration', 0.0, option_type=positive_float, long_text=_('''
Visual bell duration. Flash the screen when a bell occurs for the specified number of
seconds. Set to zero to disable.'''))
o('window_alert_on_bell', True, long_text=_('''
Request window attention on bell.
Makes the dock icon bounce on macOS or the taskbar flash on linux.'''))
o('bell_on_tab', True, long_text=_('''
Show a bell symbol on the tab if a bell occurs in one of the windows in the
tab and the window is not the currently focused window'''))
# }}}
g('window') # {{{
o('remember_window_size', True, long_text=_('''
If enabled, the window size will be remembered so that new instances of kitty
will have the same size as the previous instance. If disabled, the window will
initially have size configured by initial_window_width/height, in pixels. You
can use a suffix of "c" on the width/height values to have them interpreted as
number of cells instead of pixels.
'''))
def window_size(val):
val = val.lower()
unit = 'cells' if val.endswith('c') else 'px'
return positive_int(val.rstrip('c')), unit
o('initial_window_width', '640', option_type=window_size)
o('initial_window_height', '400', option_type=window_size)
def to_layout_names(raw):
parts = [x.strip().lower() for x in raw.split(',')]
ans = []
for p in parts:
if p == '*':
ans.extend(sorted(all_layouts))
continue
name = p.partition(':')[0]
if name not in all_layouts:
raise ValueError('The window layout {} is unknown'.format(p))
ans.append(p)
return uniq(ans)
o('enabled_layouts', '*', option_type=to_layout_names, long_text=_('''
The enabled window layouts. A comma separated list of layout names. The special
value :code:`*` means all layouts. The first listed layout will be used as the
startup layout. For a list of available layouts, see the :ref:`layouts`.
'''))
o('window_resize_step_cells', 2, option_type=positive_int, long_text=_('''
The step size (in units of cell width/cell height) to use when resizing
windows. The cells value is used for horizontal resizing and the lines value
for vertical resizing.
'''))
o('window_resize_step_lines', 2, option_type=positive_int)
o('window_border_width', 1.0, option_type=positive_float, long_text=_('''
The width (in pts) of window borders. Will be rounded to the nearest number of pixels based on screen resolution.
Note that borders are displayed only when more than one window is visible. They are meant to separate multiple windows.'''))
o('window_margin_width', 0.0, option_type=positive_float, long_text=_('''
The window margin (in pts) (blank area outside the border)'''))
o('window_padding_width', 0.0, option_type=positive_float, long_text=_('''
The window padding (in pts) (blank area between the text and the window border)'''))
o('active_border_color', '#00ff00', option_type=to_color, long_text=_('''
The color for the border of the active window'''))
o('inactive_border_color', '#cccccc', option_type=to_color, long_text=_('''
The color for the border of inactive windows'''))
o('bell_border_color', '#ff5a00', option_type=to_color, long_text=_('''
The color for the border of inactive windows in which a bell has occurred'''))
o('inactive_text_alpha', 1.0, option_type=unit_float, long_text=_('''
Fade the text in inactive windows by the specified amount (a number between
zero and one, with zero being fully faded).
'''))
# }}}
g('tabbar') # {{{
default_tab_separator = ''
def tab_separator(x):
for q in '\'"':
if x.startswith(q) and x.endswith(q):
x = x[1:-1]
break
if not x.strip():
x = ('\xa0' * len(x)) if x else default_tab_separator
return x
def tab_bar_edge(x):
return {'top': 1, 'bottom': 3}.get(x.lower(), 3)
def tab_font_style(x):
return {
'bold-italic': (True, True),
'bold': (True, False),
'italic': (False, True)
}.get(x.lower().replace('_', '-'), (False, False))
o('tab_bar_edge', 'bottom', option_type=tab_bar_edge, long_text=_('''
Which edge to show the tab bar on, top or bottom'''))
o('tab_bar_margin_width', 0.0, option_type=positive_float, long_text=_('''
The margin to the left and right of the tab bar (in pts)'''))
o('tab_bar_style', 'fade', option_type=choices('fade', 'separator'), long_text=_('''
The tab bar style, can be one of: :code:`fade` or :code:`separator`. In the fade style,
each tab's edges fade into the background color, in the separator style, tabs are
separated by a configurable separator.
'''))
def tab_fade(x):
return tuple(map(unit_float, x.split()))
o('tab_fade', '0.25 0.5 0.75 1', option_type=tab_fade, long_text=_('''
Control how each tab fades into the background when using :code:`fade` for the
:opt:`tab_bar_style`. Each number is an alpha (between zero and one) that controls
how much the corresponding cell fades into the background, with zero being no fade
and one being full fade. You can change the number of cells used by adding/removing
entries to this list.
'''))
o('tab_separator', '"{}"'.format(default_tab_separator), option_type=tab_separator, long_text=_('''
The separator between tabs in the tab bar when using :code:`separator` as the :opt:`tab_bar_style`.'''))
o('active_tab_foreground', '#000', option_type=to_color, long_text=_('''
Tab bar colors and styles'''))
o('active_tab_background', '#eee', option_type=to_color)
o('active_tab_font_style', 'bold-italic', option_type=tab_font_style)
o('inactive_tab_foreground', '#444', option_type=to_color)
o('inactive_tab_background', '#999', option_type=to_color)
o('inactive_tab_font_style', 'normal', option_type=tab_font_style)
# }}}
g('colors') # {{{
o('foreground', '#dddddd', option_type=to_color, long_text=_('''
The foreground and background colors'''))
o('background', '#000000', option_type=to_color)
o('background_opacity', 1.0, option_type=unit_float, long_text=_('''
The opacity of the background. A number between 0 and 1, where 1 is opaque and
0 is fully transparent. This will only work if supported by the OS (for
instance, when using a compositor under X11). Note that it only sets the
default background color's opacity. This is so that things like the status bar
in vim, powerline prompts, etc. still look good. But it means that if you use
a color theme with a background color in your editor, it will not be rendered
as transparent. Instead you should change the default background color in your
kitty config and not use a background color in the editor color scheme. Or use
the escape codes to set the terminals default colors in a shell script to
launch your editor. Be aware that using a value less than 1.0 is a (possibly
significant) performance hit. If you want to dynamically change transparency
of windows set dynamic_background_opacity to yes (this is off by default as it
has a performance cost)
'''))
o('dynamic_background_opacity', False)
o('dim_opacity', 0.75, option_type=unit_float, long_text=_('''
How much to dim text that has the DIM/FAINT attribute set. One means no dimming and
zero means fully dimmed (i.e. invisible).'''))
o('selection_foreground', '#000000', option_type=to_color, long_text=_('''
The foreground and background for text selected with the mouse'''))
o('selection_background', '#FFFACD', option_type=to_color)
g('colors.table')
o('color0', '#000000', long_text=_('black'), option_type=to_color)
o('color8', '#767676', option_type=to_color)
o('color1', '#cc0403', long_text=_('red'), option_type=to_color)
o('color9', '#f2201f', option_type=to_color)
o('color2', '#19cb00', long_text=_('green'), option_type=to_color)
o('color10', '#23fd00', option_type=to_color)
o('color3', '#cecb00', long_text=_('yellow'), option_type=to_color)
o('color11', '#fffd00', option_type=to_color)
o('color4', '#0d73cc', long_text=_('blue'), option_type=to_color)
o('color12', '#1a8fff', option_type=to_color)
o('color5', '#cb1ed1', long_text=_('magenta'), option_type=to_color)
o('color13', '#fd28ff', option_type=to_color)
o('color6', '#0dcdcd', long_text=_('cyan'), option_type=to_color)
o('color14', '#14ffff', option_type=to_color)
o('color7', '#dddddd', long_text=_('white'), option_type=to_color)
o('color15', '#ffffff', option_type=to_color)
dfctl = defines.default_color_table()
for i in range(16, 256):
o('color{}'.format(i), color_as_sharp(color_from_int(dfctl[i])), option_type=to_color, add_to_docs=False)
# }}}
g('advanced') # {{{
o('shell', '.', long_text=_('''
The shell program to execute. The default value of . means
to use whatever shell is set as the default shell for the current user.
Note that on macOS if you change this, you might need to add :code:`--login` to
ensure that the shell starts in interactive mode and reads its startup rc files.'''))
o('editor', '.', long_text=_('''
The console editor to use when editing the kitty config file or similar tasks.
A value of . means to use the environment variable EDITOR. Note that this
environment variable has to be set not just in your shell startup scripts but
system-wide, otherwise kitty will not see it.
'''))
o('close_on_child_death', False, long_text=_('''
Close the window when the child process (shell) exits. If no (the default), the
terminal will remain open when the child exits as long as there are still
processes outputting to the terminal (for example disowned or backgrounded
processes). If yes, the window will close as soon as the child process exits.
Note that setting it to yes means that any background processes still using the
terminal can fail silently because their stdout/stderr/stdin no longer work.
'''))
o('allow_remote_control', False, long_text=_('''
Allow other programs to control kitty. If you turn this on other programs can
control all aspects of kitty, including sending text to kitty windows,
opening new windows, closing windows, reading the content of windows, etc.
Note that this even works over ssh connections.
'''))
def startup_session(x):
if x.lower() == 'none':
return
x = os.path.expanduser(x)
x = os.path.expandvars(x)
if not os.path.isabs(x):
x = os.path.join(config_dir, x)
return x
o('startup_session', 'none', option_type=startup_session, long_text=_('''
Path to a session file to use for all kitty instances. Can be overridden
by using the :option:`kitty --session` command line option for individual
instances. See :ref:`sessions` in the kitty documentation for details. Note
that relative paths are interpreted with respect to the kitty config directory.
Environment variables in the path are expanded.
'''))
o('clipboard_control', 'write-clipboard write-primary', option_type=lambda x: frozenset(x.lower().split()), long_text=_('''
Allow programs running in kitty to read and write from the clipboard. You can
control exactly which actions are allowed. The set of possible actions is:
write-clipboard read-clipboard write-primary read-primary
The default is to allow writing to the clipboard and primary selection. Note
that enabling the read functionality is a security risk as it means that any
program, even one running on a remote server via SSH can read your clipboard.
'''))
o('term', 'xterm-kitty', long_text=_('''
The value of the TERM environment variable to set. Changing this can break
many terminal programs, only change it if you know what you are doing, not
because you read some advice on Stack Overflow to change it.
'''))
# }}}
g('os') # {{{
def macos_titlebar_color(x):
x = x.strip('"')
if x == 'system':
return 0
if x == 'background':
return 1
return (color_as_int(to_color(x)) << 8) | 2
o('macos_titlebar_color', 'system', option_type=macos_titlebar_color, long_text=_('''
Change the color of the kitty window's titlebar on macOS. A value of :code:`system`
means to use the default system color, a value of :code:`background` means to use
the background color of the currently active window and finally you can use
an arbitrary color, such as :code:`#12af59` or :code:`red`. WARNING: This option works by
using a hack, as there is no proper Cocoa API for it. It sets the background
color of the entire window and makes the titlebar transparent. As such it is
incompatible with :opt:`background_opacity`. If you want to use both, you are
probably better off just hiding the titlebar with :opt:`macos_hide_titlebar`.
'''))
o('macos_hide_titlebar', False, long_text=_('''
Hide the kitty window's title bar on macOS.'''))
o('x11_hide_window_decorations', False, long_text=_('''
Hide the window decorations (title bar and window borders) on X11 and Wayland.
Whether this works and exactly what effect it has depends on the window
manager, as it is the job of the window manager/compositor to draw window
decorations.'''))
o('macos_option_as_alt', True, long_text=_('''
Use the option key as an alt key. With this set to no, kitty will use
the macOS native :kbd:`Option+Key` = unicode character behavior. This will
break any :kbd:`Alt+key` keyboard shortcuts in your terminal programs, but you
can use the macOS unicode input technique.
'''))
o('macos_hide_from_tasks', False, long_text=_('''
Hide the kitty window from running tasks (:kbd:`Option+Tab`) on macOS.
'''))
o('macos_quit_when_last_window_closed', False, long_text=_('''
Have kitty quit when all the top-level windows are closed. By default,
kitty will stay running, even with no open windows, as is the expected
behavior on macOS.
'''))
# }}}
g('shortcuts') # {{{
o('kitty_mod', 'ctrl+shift', option_type=to_modifiers, long_text=_('''
The value of :code:`kitty_mod` is used as the modifier for all default shortcuts, you
can change it in your kitty.conf to change the modifiers for all the default
shortcuts.'''))
o('clear_all_shortcuts', False, long_text=_('''
You can have kitty remove all shortcut definition seen up to this point. Useful, for
instance, to remove the default shortcuts.'''))
g('shortcuts.clipboard') # {{{
if is_macos:
k('copy_to_clipboard', 'cmd+c', 'copy_to_clipboard', _('Copy to clipboard'), add_to_docs=False)
k('paste_from_clipboard', 'cmd+v', 'paste_from_clipboard', _('Paste from clipboard'), add_to_docs=False)
k('copy_to_clipboard', 'kitty_mod+c', 'copy_to_clipboard', _('Copy to clipboard'))
k('paste_from_clipboard', 'kitty_mod+v', 'paste_from_clipboard', _('Paste from clipboard'))
k('paste_from_selection', 'kitty_mod+s', 'paste_from_selection', _('Paste from selection'))
k('paste_from_selection', 'shift+insert', 'paste_from_selection', _('Paste from selection'))
k('pass_selection_to_program', 'kitty_mod+o', 'pass_selection_to_program', _('Pass selection to program'), long_text=_('''
You can also pass the contents of the current selection to any program using
:code:`pass_selection_to_program`. By default, the system's open program is used, but
you can specify your own, for example::
map kitty_mod+o pass_selection_to_program firefox
You can pass the current selection to a terminal program running in a new kitty
window, by using the @selection placeholder::
map kitty_mod+y new_window less @selection
'''))
# }}}
g('shortcuts.scrolling') # {{{
k('scroll_line_up', 'kitty_mod+up', 'scroll_line_up', _('Scroll line up'))
k('scroll_line_up', 'kitty_mod+k', 'scroll_line_up')
k('scroll_line_down', 'kitty_mod+down', 'scroll_line_down', _('Scroll line down'))
k('scroll_line_down', 'kitty_mod+j', 'scroll_line_down')
k('scroll_page_up', 'kitty_mod+page_up', 'scroll_page_up', _('Scroll page up'))
k('scroll_page_down', 'kitty_mod+page_down', 'scroll_page_down', _('Scroll page down'))
k('scroll_home', 'kitty_mod+home', 'scroll_home', _('Scroll to top'))
k('scroll_end', 'kitty_mod+end', 'scroll_end', _('Scroll to bottom'))
k('show_scrollback', 'kitty_mod+h', 'show_scrollback', _('Browse scrollback buffer in less'), long_text=_('''
You can send the contents of the current screen + history buffer as stdin to an arbitrary program using
the placeholders @text (which is the plain text) and @ansi (which includes text styling escape codes).
For only the current screen, use @screen or @ansi_screen.
For example, the following command opens the scrollback buffer in less in a new window::
map kitty_mod+y new_window @ansi less +G -R
'''))
# }}}
g('shortcuts.window') # {{{
k('new_window', 'kitty_mod+enter', 'new_window', _(''), long_text=_('''
You can open a new window running an arbitrary program, for example::
map kitty_mod+y new_window mutt
You can open a new window with the current working directory set to the
working directory of the current window using::
map ctrl+alt+enter new_window_with_cwd
'''))
if is_macos:
k('new_os_window', 'cmd+n', 'new_os_window', _('New OS window'))
k('new_os_window', 'kitty_mod+n', 'new_os_window', _('New OS window'))
k('close_window', 'kitty_mod+w', 'close_window', _('Close window'))
k('next_window', 'kitty_mod+]', 'next_window', _('Next window'))
k('previous_window', 'kitty_mod+[', 'previous_window', _('Previous window'))
k('move_window_forward', 'kitty_mod+f', 'move_window_forward', _('Move window forward'))
k('move_window_backward', 'kitty_mod+b', 'move_window_backward', _('Move window backward'))
k('move_window_to_top', 'kitty_mod+`', 'move_window_to_top', _('Move window to top'))
k('start_resizing_window', 'kitty_mod+r', 'start_resizing_window', _('Start resizing window'))
k('first_window', 'kitty_mod+1', 'first_window', _('First window'))
k('second_window', 'kitty_mod+2', 'second_window', _('Second window'))
k('third_window', 'kitty_mod+3', 'third_window', _('Third window'))
k('fourth_window', 'kitty_mod+4', 'fourth_window', _('Fourth window'))
k('fifth_window', 'kitty_mod+5', 'fifth_window', _('Fifth window'))
k('sixth_window', 'kitty_mod+6', 'sixth_window', _('Sixth window'))
k('seventh_window', 'kitty_mod+7', 'seventh_window', _('Seventh window'))
k('eighth_window', 'kitty_mod+8', 'eighth_window', _('Eight window'))
k('ninth_window', 'kitty_mod+9', 'ninth_window', _('Ninth window'))
k('tenth_window', 'kitty_mod+0', 'tenth_window', _('Tenth window'))
# }}}
g('shortcuts.tab') # {{{
k('next_tab', 'kitty_mod+right', 'next_tab', _('Next tab'))
k('previous_tab', 'kitty_mod+left', 'previous_tab', _('Previous tab'))
k('new_tab', 'kitty_mod+t', 'new_tab', _('New tab'))
k('close_tab', 'kitty_mod+q', 'close_tab', _('Close tab'))
k('move_tab_forward', 'kitty_mod+.', 'move_tab_forward', _('Move tab forward'))
k('move_tab_backward', 'kitty_mod+,', 'move_tab_backward', _('Move tab backward'))
k('set_tab_title', 'kitty_mod+alt+t', 'set_tab_title', _('Set tab title'))
# }}}
g('shortcuts.layout') # {{{
k('next_layout', 'kitty_mod+l', 'next_layout', _('Next layout'))
# }}}
g('shortcuts.fonts') # {{{
k('increase_font_size', 'kitty_mod+equal', 'change_font_size all +2.0', _('Increase font size'))
k('decrease_font_size', 'kitty_mod+minus', 'change_font_size all -2.0', _('Decrease font size'))
k('reset_font_size', 'kitty_mod+backspace', 'change_font_size all 0', _('Reset font size'))
# }}}
g('shortcuts.selection') # {{{
k('open_url', 'kitty_mod+e', 'kitten hints', _('Open URL'), _('''
Open a currently visible URL using the keyboard. The program used to open the
URL is specified in :opt:`open_url_with`.'''))
k('insert_selected_path', 'kitty_mod+p>f', 'kitten hints --type path --program -', _('Insert selected path'), long_text=_('''
Select a path/filename and insert it into the terminal. Useful, for instance to
run git commands on a filename output from a previous git command.'''))
k('open_selected_path', 'kitty_mod+p>shift+f', 'kitten hints --type path', _('Open selected path'), long_text=_('''
Select a path/filename and open it with the default open program.'''))
k('insert_selected_line', 'kitty_mod+p>l', 'kitten hints --type line --program -', _('Insert selected line'), long_text=_('''
Select a line of text and insert it into the terminal. Use for the
output of things like: ls -1'''))
k('insert_selected_word', 'kitty_mod+p>w', 'kitten hints --type word --program -', _('Insert selected word'), long_text=_('''
Select words and insert into terminal.'''))
k('insert_selected_hash', 'kitty_mod+p>h', 'kitten hints --type hash --program -', _('Insert selected hash'), long_text=_('''
Select something that looks like a hash and insert it into the terminal.
Useful with git, which uses sha1 hashes to identify commits'''))
# }}}
g('shortcuts.misc') # {{{
k('toggle_fullscreen', 'kitty_mod+f11', 'toggle_fullscreen', _('Toggle fullscreen'))
k('input_unicode_character', 'kitty_mod+u', 'input_unicode_character', _('Unicode input'))
k('edit_config_file', 'kitty_mod+f2', 'edit_config_file', _('Edit config file'))
k('kitty_shell', 'kitty_mod+escape', 'kitty_shell window', _('Open the kitty command shell'), long_text=_('''
Open the kitty shell in a new window/tab/overlay/os_window to control kitty using commands.'''))
k('increase_background_opacity', 'kitty_mod+a>m', 'set_background_opacity +0.1', _('Increase background opacity'))
k('decrease_background_opacity', 'kitty_mod+a>l', 'set_background_opacity -0.1', _('Decrease background opacity'))
k('full_background_opacity', 'kitty_mod+a>1', 'set_background_opacity 1', _('Make background fully opaque'))
k('reset_background_opacity', 'kitty_mod+a>d', 'set_background_opacity default', _('Reset background opacity'))
k('send_text', 'ctrl+shift+alt+h', 'send_text all Hello World', _('Send arbitrary text on key presses'),
add_to_default=False, long_text=_('''
You can tell kitty to send arbitrary (UTF-8) encoded text to
the client program when pressing specified shortcut keys. For example::
map ctrl+alt+a send_text all Special text
This will send "Special text" when you press the :kbd:`ctrl+alt+a` key
combination. The text to be sent is a python string literal so you can use
escapes like :code:`\\x1b` to send control codes or :code:`\\u21fb` to send
unicode characters (or you can just input the unicode characters directly as
UTF-8 text). The first argument to :code:`send_text` is the keyboard modes in which to
activate the shortcut. The possible values are :code:`normal` or :code:`application` or :code:`kitty`
or a comma separated combination of them. The special keyword :code:`all` means all
modes. The modes :code:`normal` and :code:`application` refer to the DECCKM cursor key mode for
terminals, and :code:`kitty` refers to the special kitty extended keyboard protocol.
Another example, that outputs a word and then moves the cursor to the start of
the line (same as pressing the Home key)::
map ctrl+alt+a send_text normal Word\\x1b[H
map ctrl+alt+a send_text application Word\\x1bOH
'''))
# }}}
# }}}
type_map = {o.name: o.option_type for o in all_options.values() if hasattr(o, 'option_type')}

View File

@@ -4,19 +4,17 @@
import os
import pwd
import shlex
import sys
from collections import namedtuple
from .fast_data_types import set_boss as set_c_boss
appname = 'kitty'
version = (0, 10, 1)
version = (0, 11, 1)
str_version = '.'.join(map(str, version))
_plat = sys.platform.lower()
is_macos = 'darwin' in _plat
base = os.path.dirname(os.path.abspath(__file__))
editor = shlex.split(os.environ.get('EDITOR', 'vim'))
ScreenGeometry = namedtuple('ScreenGeometry', 'xstart ystart xnum ynum dx dy')

View File

@@ -178,7 +178,7 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) {
}
PyObject*
create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation) {
create_fallback_face(PyObject *base_face, CPUCell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation, FONTS_DATA_HANDLE fg UNUSED) {
CTFace *self = (CTFace*)base_face;
CTFontRef new_font;
if (emoji_presentation) new_font = CTFontCreateWithName((CFStringRef)@"AppleColorEmoji", self->scaled_point_sz, NULL);
@@ -214,14 +214,14 @@ is_glyph_empty(PyObject *s, glyph_index g) {
}
static inline float
scaled_point_sz() {
return ((global_state.logical_dpi_x + global_state.logical_dpi_y) / 144.0) * global_state.font_sz_in_pts;
scaled_point_sz(FONTS_DATA_HANDLE fg) {
return ((fg->logical_dpi_x + fg->logical_dpi_y) / 144.0) * fg->font_sz_in_pts;
}
bool
set_size_for_face(PyObject *s, unsigned int UNUSED desired_height, bool force) {
set_size_for_face(PyObject *s, unsigned int UNUSED desired_height, bool force, FONTS_DATA_HANDLE fg) {
CTFace *self = (CTFace*)s;
float sz = scaled_point_sz();
float sz = scaled_point_sz(fg);
if (!force && self->scaled_point_sz == sz) return true;
CTFontRef new_font = CTFontCreateCopyWithAttributes(self->ct_font, sz, NULL, NULL);
if (new_font == NULL) fatal("Out of memory");
@@ -282,17 +282,17 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u
}
PyObject*
face_from_descriptor(PyObject *descriptor) {
face_from_descriptor(PyObject *descriptor, FONTS_DATA_HANDLE fg) {
CTFontDescriptorRef desc = font_descriptor_from_python(descriptor);
if (!desc) return NULL;
CTFontRef font = CTFontCreateWithFontDescriptor(desc, scaled_point_sz(), NULL);
CTFontRef font = CTFontCreateWithFontDescriptor(desc, scaled_point_sz(fg), NULL);
CFRelease(desc); desc = NULL;
if (!font) { PyErr_SetString(PyExc_ValueError, "Failed to create CTFont object"); return NULL; }
return (PyObject*) ct_face(font);
}
PyObject*
face_from_path(const char *path, int UNUSED index) {
face_from_path(const char *path, int UNUSED index, FONTS_DATA_HANDLE fg UNUSED) {
CFStringRef s = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, s, kCFURLPOSIXPathStyle, false);
CGDataProviderRef dp = CGDataProviderCreateWithURL(url);
@@ -303,7 +303,7 @@ face_from_path(const char *path, int UNUSED index) {
}
PyObject*
specialize_font_descriptor(PyObject *base_descriptor) {
specialize_font_descriptor(PyObject *base_descriptor, FONTS_DATA_HANDLE fg UNUSED) {
Py_INCREF(base_descriptor);
return base_descriptor;
}
@@ -371,7 +371,7 @@ render_glyphs(CTFontRef font, unsigned int width, unsigned int height, unsigned
}
static inline bool
do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, bool allow_resize) {
do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, bool allow_resize, FONTS_DATA_HANDLE fg) {
unsigned int canvas_width = cell_width * num_cells;
CGRect br = CTFontGetBoundingRectsForGlyphs(ct_font, kCTFontOrientationHorizontal, glyphs, boxes, num_glyphs);
if (allow_resize) {
@@ -382,7 +382,7 @@ do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_g
CGFloat sz = CTFontGetSize(ct_font);
sz *= canvas_width / right;
CTFontRef new_font = CTFontCreateCopyWithAttributes(ct_font, sz, NULL, NULL);
bool ret = do_render(new_font, bold, italic, info, hb_positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline, was_colored, false);
bool ret = do_render(new_font, bold, italic, info, hb_positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline, was_colored, false, fg);
CFRelease(new_font);
return ret;
}
@@ -408,10 +408,10 @@ do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_g
}
bool
render_glyphs_in_cells(PyObject *s, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored) {
render_glyphs_in_cells(PyObject *s, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE fg) {
CTFace *self = (CTFace*)s;
for (unsigned i=0; i < num_glyphs; i++) glyphs[i] = info[i].codepoint;
return do_render(self->ct_font, bold, italic, info, hb_positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline, was_colored, true);
return do_render(self->ct_font, bold, italic, info, hb_positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline, was_colored, true, fg);
}

View File

@@ -136,7 +136,7 @@ END_ALLOW_CASE_RANGE
}
void
apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count) {
apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count) {
#define RANGE for(unsigned c = 0; c < cell_count; c++, cell++)
#define SET(shift) RANGE { cell->attrs |= (1 << shift); } break;
#define RESET(shift) RANGE { cell->attrs &= ~(1 << shift); } break;
@@ -148,7 +148,7 @@ apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *para
unsigned int i = 0, attr;
if (!count) { params[0] = 0; count = 1; }
while (i < count) {
Cell *cell = first_cell;
GPUCell *cell = first_cell;
attr = params[i++];
switch(attr) {
case 0:

View File

@@ -17,6 +17,7 @@
#include <stddef.h>
#include <termios.h>
#include <signal.h>
#include <fcntl.h>
#ifdef WITH_PROFILER
#include <gperftools/profiler.h>
#endif
@@ -103,7 +104,61 @@ stop_profiler(PyObject UNUSED *self) {
}
#endif
static inline bool
put_tty_in_raw_mode(int fd, const struct termios* termios_p) {
struct termios raw_termios = *termios_p;
cfmakeraw(&raw_termios);
raw_termios.c_cc[VMIN] = 1; raw_termios.c_cc[VTIME] = 0;
if (tcsetattr(fd, TCSAFLUSH, &raw_termios) != 0) { PyErr_SetFromErrno(PyExc_OSError); return false; }
return true;
}
static PyObject*
open_tty(PyObject *self UNUSED, PyObject *args UNUSED) {
int fd = open("/dev/tty", O_RDWR | O_NONBLOCK | O_CLOEXEC | O_NOCTTY);
struct termios *termios_p = calloc(1, sizeof(struct termios));
if (!termios_p) return PyErr_NoMemory();
if (tcgetattr(fd, termios_p) != 0) { free(termios_p); PyErr_SetFromErrno(PyExc_OSError); return NULL; }
if (!put_tty_in_raw_mode(fd, termios_p)) { free(termios_p); return NULL; }
return Py_BuildValue("iN", fd, PyLong_FromVoidPtr(termios_p));
}
#define TTY_ARGS \
PyObject *tp; int fd; \
if (!PyArg_ParseTuple(args, "iO!", &fd, &PyLong_Type, &tp)) return NULL; \
struct termios *termios_p = PyLong_AsVoidPtr(tp);
static PyObject*
normal_tty(PyObject *self UNUSED, PyObject *args) {
TTY_ARGS
if (tcsetattr(fd, TCSAFLUSH, termios_p) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
Py_RETURN_NONE;
}
static PyObject*
raw_tty(PyObject *self UNUSED, PyObject *args) {
TTY_ARGS
if (!put_tty_in_raw_mode(fd, termios_p)) return NULL;
Py_RETURN_NONE;
}
static PyObject*
close_tty(PyObject *self UNUSED, PyObject *args) {
TTY_ARGS
tcsetattr(fd, TCSAFLUSH, termios_p); // deliberately ignore failure
free(termios_p);
close(fd);
Py_RETURN_NONE;
}
#undef TTY_ARGS
static PyMethodDef module_methods[] = {
{"open_tty", open_tty, METH_NOARGS, ""},
{"normal_tty", normal_tty, METH_VARARGS, ""},
{"raw_tty", raw_tty, METH_VARARGS, ""},
{"close_tty", close_tty, METH_VARARGS, ""},
{"set_iutf8", (PyCFunction)pyset_iutf8, METH_VARARGS, ""},
{"thread_write", (PyCFunction)cm_thread_write, METH_VARARGS, ""},
{"parse_bytes", (PyCFunction)parse_bytes, METH_VARARGS, ""},

View File

@@ -7,13 +7,12 @@
#pragma once
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdint.h>
#include <stdbool.h>
#include <poll.h>
#include <pthread.h>
#define PY_SSIZE_T_CLEAN
#include <Python.h>
// Required minimum OpenGL version
#define OPENGL_REQUIRED_VERSION_MAJOR 3
#define OPENGL_REQUIRED_VERSION_MINOR 3
@@ -80,7 +79,7 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
(c)->reverse = (a >> REVERSE_SHIFT) & 1; (c)->strikethrough = (a >> STRIKE_SHIFT) & 1; (c)->dim = (a >> DIM_SHIFT) & 1;
#define COPY_CELL(src, s, dest, d) \
(dest)->cells[d] = (src)->cells[s];
(dest)->cpu_cells[d] = (src)->cpu_cells[s]; (dest)->gpu_cells[d] = (src)->gpu_cells[s];
#define COPY_SELF_CELL(s, d) COPY_CELL(self, s, self, d)
@@ -140,15 +139,19 @@ typedef struct {
color_type fg, bg, decoration_fg;
sprite_index sprite_x, sprite_y, sprite_z;
attrs_type attrs;
// The following are only needed on the CPU, not the GPU
} GPUCell;
typedef struct {
char_type ch;
combining_type cc_idx[2];
} Cell;
} CPUCell;
typedef struct {
PyObject_HEAD
Cell *cells;
GPUCell *gpu_cells;
CPUCell *cpu_cells;
index_type xnum, ynum;
bool continued, needs_free, has_dirty_text;
} Line;
@@ -157,14 +160,16 @@ typedef struct {
typedef struct {
PyObject_HEAD
Cell *buf;
GPUCell *gpu_cell_buf;
CPUCell *cpu_cell_buf;
index_type xnum, ynum, *line_map, *scratch;
line_attrs_type *line_attrs;
Line *line;
} LineBuf;
typedef struct {
Cell *cells;
GPUCell *gpu_cells;
CPUCell *cpu_cells;
line_attrs_type *line_attrs;
} HistoryBufSegment;
@@ -206,9 +211,18 @@ typedef struct {
bool dirty;
uint32_t color_table[256];
uint32_t orig_color_table[256];
DynamicColor dynamic_color_stack[10];
size_t dynamic_color_stack_idx;
DynamicColor configured, overridden;
} ColorProfile;
typedef struct {
unsigned int width, height;
} CellPixelSize;
typedef struct {int x;} *SPRITE_MAP_HANDLE;
#define FONTS_DATA_HEAD SPRITE_MAP_HANDLE sprite_map; double logical_dpi_x, logical_dpi_y, font_sz_in_pts; unsigned int cell_width, cell_height;
typedef struct {FONTS_DATA_HEAD} *FONTS_DATA_HANDLE;
#define PARSER_BUF_SZ (8 * 1024)
#define READ_BUF_SZ (1024*1024)
@@ -219,10 +233,10 @@ typedef struct {
for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \
COPY_CELL(line, __i__ + (num), line, __i__) \
} \
if ((((line)->cells[(at)].attrs) & WIDTH_MASK) != 1) { \
(line)->cells[(at)].ch = BLANK_CHAR; \
(line)->cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \
clear_sprite_position((line)->cells[(at)]); \
if ((((line)->gpu_cells[(at)].attrs) & WIDTH_MASK) != 1) { \
(line)->cpu_cells[(at)].ch = BLANK_CHAR; \
(line)->gpu_cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \
clear_sprite_position((line)->gpu_cells[(at)]); \
}\
}
@@ -253,7 +267,7 @@ Cursor* cursor_copy(Cursor*);
void cursor_copy_to(Cursor *src, Cursor *dest);
void cursor_reset_display_attrs(Cursor*);
void cursor_from_sgr(Cursor *self, unsigned int *params, unsigned int count);
void apply_sgr_to_cells(Cell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count);
void apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, unsigned int *params, unsigned int count);
const char* cursor_as_sgr(Cursor*, Cursor*);
double monotonic();
@@ -263,6 +277,8 @@ bool set_iutf8(int, bool);
color_type colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval);
void copy_color_table_to_buffer(ColorProfile *self, color_type *address, int offset, size_t stride);
void colorprofile_push_dynamic_colors(ColorProfile*);
void colorprofile_pop_dynamic_colors(ColorProfile*);
void set_mouse_cursor(MouseShape);
void mouse_event(int, int);
@@ -273,3 +289,5 @@ void fake_scroll(int, bool);
void set_special_key_combo(int glfw_key, int mods);
void on_key_input(int key, int scancode, int action, int mods, const char*, int);
void request_window_attention(id_type, bool);
SPRITE_MAP_HANDLE alloc_sprite_map(unsigned int, unsigned int);
SPRITE_MAP_HANDLE free_sprite_map(SPRITE_MAP_HANDLE);

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