Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4af0599cc | ||
|
|
de4384a420 | ||
|
|
a33ebce3c9 | ||
|
|
219d3161d9 | ||
|
|
2e07159461 | ||
|
|
23d0f5ce32 | ||
|
|
f4792a1e3d | ||
|
|
2a5ef628fa | ||
|
|
4b259dd719 | ||
|
|
c127476c4e | ||
|
|
beded75547 | ||
|
|
74a6cdb3bb | ||
|
|
ba554fb01d | ||
|
|
39c98e83da | ||
|
|
73660f7119 | ||
|
|
46b4f33850 | ||
|
|
ad8e16aac0 | ||
|
|
ae6e082419 | ||
|
|
f46d3057ba | ||
|
|
8e0e4b8fd1 | ||
|
|
76da315f81 | ||
|
|
95de39feb9 | ||
|
|
c0f790c928 | ||
|
|
6fbb953aa4 | ||
|
|
30b38e9fa0 | ||
|
|
d20e801793 | ||
|
|
fe65b346c1 | ||
|
|
c5da1c0f1b | ||
|
|
3fd2f03f11 | ||
|
|
4ad4e28a9c | ||
|
|
d3edd2e73f | ||
|
|
3d37348c2b | ||
|
|
8a3e81b7ba | ||
|
|
a29c317217 | ||
|
|
48847cdaf8 | ||
|
|
a59cda8b60 | ||
|
|
e70b5114fb | ||
|
|
d2ab7fbb36 | ||
|
|
70352ca722 | ||
|
|
be28e7bf46 | ||
|
|
6b2b8e0208 | ||
|
|
c997dc8ea7 | ||
|
|
f52afbb396 | ||
|
|
a20e7e2a6b |
@@ -3,12 +3,38 @@ 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/tree/master/kittens/diff>`_.
|
||||
<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
|
||||
@@ -32,11 +58,12 @@ version 0.10.0 [2018-05-21]
|
||||
|
||||
- Allow programs running in kitty to `read/write from the clipboard
|
||||
<https://github.com/kovidgoyal/kitty/commit/889ca7791244253cb08fbc3eca8883a87fb943a7>`_.
|
||||
By default only writing is allowed.
|
||||
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.
|
||||
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>`_).
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
: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+.`]
|
||||
@@ -32,11 +37,6 @@
|
||||
: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_run_kitten_text_hints: pass:quotes[`ctrl+shift+e`]
|
||||
:sc_run_kitten_text_hints_type_line_program: pass:quotes[`ctrl+shift+p → l`]
|
||||
:sc_run_kitten_text_hints_type_path: pass:quotes[`ctrl+shift+p → shift+f`]
|
||||
:sc_run_kitten_text_hints_type_path_program: pass:quotes[`ctrl+shift+p → f`]
|
||||
:sc_run_kitten_text_hints_type_word_program: pass:quotes[`ctrl+shift+p → w`]
|
||||
: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`]
|
||||
@@ -44,6 +44,10 @@
|
||||
: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`]
|
||||
@@ -74,7 +78,7 @@ 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] and link:#hints[Hints].
|
||||
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.
|
||||
@@ -154,7 +158,7 @@ just `kitty`.
|
||||
|
||||
* openSUSE: https://build.opensuse.org/package/show/X11:terminals/kitty
|
||||
|
||||
* Debian: https://packages.qa.debian.org/k/kitty/news/20180419T070013Z.html
|
||||
* Debian: https://packages.debian.org/buster/kitty
|
||||
|
||||
=== macOS packages
|
||||
|
||||
@@ -262,6 +266,10 @@ windows are:
|
||||
|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}
|
||||
|===
|
||||
|
||||
|
||||
@@ -396,7 +404,8 @@ layout tall
|
||||
cd ~
|
||||
# Create a window and run the specified command in it
|
||||
launch zsh
|
||||
launch vim
|
||||
# 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
|
||||
@@ -560,8 +569,16 @@ 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.
|
||||
Either install kitty on the server, or simply copy over the terminfo files,
|
||||
using:
|
||||
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
|
||||
@@ -571,17 +588,6 @@ 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.
|
||||
|
||||
In the meantime, you can automate it by using a simple script called, for example,
|
||||
`~/bin/myssh`
|
||||
|
||||
....
|
||||
#!/bin/sh
|
||||
infocmp xterm-kitty | ssh $1 tic -x -o \~/.terminfo /dev/stdin && ssh $1
|
||||
....
|
||||
|
||||
Then you can use `myssh server` to log in to `server` with the terminfo file
|
||||
being automatically available.
|
||||
|
||||
|
||||
=== How do I change the colors in a running kitty instance?
|
||||
|
||||
|
||||
@@ -24,10 +24,11 @@ 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 third-party programs that use the kitty graphics protocol:
|
||||
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::[]
|
||||
|
||||
|
||||
0
kittens/clipboard/__init__.py
Normal file
0
kittens/clipboard/__init__.py
Normal file
76
kittens/clipboard/main.py
Normal file
76
kittens/clipboard/main.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import sys
|
||||
from kitty.cli import parse_args
|
||||
|
||||
from ..tui.handler import Handler
|
||||
from ..tui.loop import Loop
|
||||
|
||||
|
||||
class Clipboard(Handler):
|
||||
|
||||
def __init__(self, data_to_send, args):
|
||||
self.args = args
|
||||
self.print_on_fail = None
|
||||
self.clipboard_contents = None
|
||||
self.data_to_send = data_to_send
|
||||
|
||||
def initialize(self):
|
||||
if self.data_to_send is not None:
|
||||
self.cmd.write_to_clipboard(self.data_to_send, self.args.use_primary)
|
||||
if not self.args.get_clipboard:
|
||||
self.quit_loop(0)
|
||||
return
|
||||
self.cmd.request_from_clipboard(self.args.use_primary)
|
||||
|
||||
def on_clipboard_response(self, text, from_primary=False):
|
||||
self.clipboard_contents = text
|
||||
self.quit_loop(0)
|
||||
|
||||
|
||||
OPTIONS = r'''
|
||||
--get-clipboard
|
||||
default=False
|
||||
type=bool-set
|
||||
Output the current contents of the clipboard to stdout. Note that this
|
||||
will not work if you have not enabled the option to allow reading the clipboard
|
||||
in kitty.conf
|
||||
|
||||
|
||||
--use-primary
|
||||
default=False
|
||||
type=bool-set
|
||||
Use the primary selection rather than the clipboard on systems that support it,
|
||||
such as X11.
|
||||
'''.format
|
||||
|
||||
|
||||
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')
|
||||
if items:
|
||||
raise SystemExit('Unrecognized extra command line arguments')
|
||||
data = None
|
||||
if not sys.stdin.isatty():
|
||||
data = sys.stdin.buffer.read()
|
||||
sys.stdin = open('/dev/tty', 'r')
|
||||
loop = Loop()
|
||||
handler = Clipboard(data, args)
|
||||
loop.loop(handler)
|
||||
if loop.return_code == 0 and handler.clipboard_contents:
|
||||
sys.stdout.write(handler.clipboard_contents)
|
||||
sys.stdout.flush()
|
||||
if handler.print_on_fail:
|
||||
print(handler.print_on_fail, file=sys.stderr)
|
||||
input('Press Enter to quit')
|
||||
raise SystemExit(loop.return_code)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
@@ -79,6 +79,13 @@ directory contents.
|
||||
[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`:
|
||||
@@ -103,3 +110,18 @@ 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 :)
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
import os
|
||||
|
||||
from kitty.config_utils import (
|
||||
init_config, load_config as _load_config, merge_dicts, parse_config_base,
|
||||
python_string, resolve_config, to_color
|
||||
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.constants import config_dir
|
||||
from kitty.rgb import color_as_sgr
|
||||
@@ -58,14 +59,46 @@ for name in (
|
||||
' highlight_removed_bg highlight_added_bg'
|
||||
).split():
|
||||
type_map[name] = to_color
|
||||
func_with_args, args_funcs = key_func()
|
||||
|
||||
|
||||
def special_handling(*a):
|
||||
pass
|
||||
@func_with_args('scroll_by')
|
||||
def parse_scroll_by(func, rest):
|
||||
try:
|
||||
return func, int(rest)
|
||||
except Exception:
|
||||
return func, 1
|
||||
|
||||
|
||||
@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'}:
|
||||
rest = 'start'
|
||||
return func, rest
|
||||
|
||||
|
||||
@func_with_args('change_context')
|
||||
def parse_change_context(func, rest):
|
||||
rest = rest.lower()
|
||||
if rest in {'all', 'default'}:
|
||||
return func, rest
|
||||
try:
|
||||
amount = int(rest)
|
||||
except Exception:
|
||||
amount = 5
|
||||
return func, amount
|
||||
|
||||
|
||||
def special_handling(key, val, ans):
|
||||
if key == 'map':
|
||||
action, *key_def = parse_kittens_key(val, args_funcs)
|
||||
ans['key_definitions'][tuple(key_def)] = action
|
||||
return True
|
||||
|
||||
|
||||
def parse_config(lines, check_keys=True):
|
||||
ans = {}
|
||||
ans = {'key_definitions': {}}
|
||||
parse_config_base(
|
||||
lines,
|
||||
defaults,
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
# file.abc use a setting of xyz:abc
|
||||
syntax_aliases pyj:py recipe:py
|
||||
|
||||
# The pygments color scheme to use. See https://help.farbox.com/pygments.html
|
||||
# for a list of schemes.
|
||||
# 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.
|
||||
@@ -36,3 +36,23 @@ 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
|
||||
|
||||
@@ -57,7 +57,7 @@ class DiffFormatter(Formatter):
|
||||
def format(self, tokensource, outfile):
|
||||
for ttype, value in tokensource:
|
||||
not_found = True
|
||||
if value != '\n':
|
||||
if value.rstrip('\n'):
|
||||
while ttype and not_found:
|
||||
tok = self.styles.get(ttype)
|
||||
if tok is None:
|
||||
|
||||
@@ -11,9 +11,7 @@ from gettext import gettext as _
|
||||
|
||||
from kitty.cli import CONFIG_HELP, appname, parse_args
|
||||
from kitty.fast_data_types import wcswidth
|
||||
from kitty.key_encoding import (
|
||||
DOWN, END, ESCAPE, HOME, PAGE_DOWN, PAGE_UP, RELEASE, UP
|
||||
)
|
||||
from kitty.key_encoding import RELEASE
|
||||
|
||||
from ..tui.handler import Handler
|
||||
from ..tui.images import ImageManager
|
||||
@@ -63,6 +61,36 @@ class DiffHandler(Handler):
|
||||
self.current_context_count = self.original_context_count = self.opts.num_context_lines
|
||||
self.highlighting_done = False
|
||||
self.restore_position = None
|
||||
for key_def, action in self.opts.key_definitions.items():
|
||||
self.add_shortcut(action, *key_def)
|
||||
|
||||
def perform_action(self, action):
|
||||
func, args = action
|
||||
if func == 'quit':
|
||||
self.quit_loop(0)
|
||||
return
|
||||
if self.state <= DIFFED:
|
||||
if func == 'scroll_by':
|
||||
return self.scroll_lines(*args)
|
||||
if func == 'scroll_to':
|
||||
where = args[0]
|
||||
if 'change' in where:
|
||||
return self.scroll_to_next_change(backwards='prev' in where)
|
||||
if 'page' in where:
|
||||
amt = self.num_lines * (1 if 'next' in where else -1)
|
||||
else:
|
||||
amt = len(self.diff_lines) * (1 if 'end' in where else -1)
|
||||
return self.scroll_lines(amt)
|
||||
if func == 'change_context':
|
||||
new_ctx = self.current_context_count
|
||||
to = args[0]
|
||||
if to == 'all':
|
||||
new_ctx = 100000
|
||||
elif to == 'default':
|
||||
new_ctx = self.original_context_count
|
||||
else:
|
||||
new_ctx += to
|
||||
return self.change_context_count(new_ctx)
|
||||
|
||||
def create_collection(self):
|
||||
self.start_job('collect', create_collection, self.left, self.right)
|
||||
@@ -236,7 +264,7 @@ class DiffHandler(Handler):
|
||||
return
|
||||
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), fg=self.opts.margin_fg)
|
||||
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),
|
||||
@@ -258,46 +286,16 @@ class DiffHandler(Handler):
|
||||
self.draw_screen()
|
||||
|
||||
def on_text(self, text, in_bracketed_paste=False):
|
||||
if text == 'q':
|
||||
if self.state <= DIFFED:
|
||||
self.quit_loop(0)
|
||||
return
|
||||
if self.state is DIFFED:
|
||||
if text in 'jk':
|
||||
self.scroll_lines(1 if text == 'j' else -1)
|
||||
return
|
||||
if text in 'a+-=':
|
||||
new_ctx = self.current_context_count
|
||||
if text == 'a':
|
||||
new_ctx = 100000
|
||||
elif text == '=':
|
||||
new_ctx = self.original_context_count
|
||||
else:
|
||||
new_ctx += (-1 if text == '-' else 1) * 5
|
||||
self.change_context_count(new_ctx)
|
||||
if text in 'np':
|
||||
self.scroll_to_next_change(backwards=text == 'p')
|
||||
return
|
||||
action = self.shortcut_action(text)
|
||||
if action is not None:
|
||||
return self.perform_action(action)
|
||||
|
||||
def on_key(self, key_event):
|
||||
if key_event.type is RELEASE:
|
||||
return
|
||||
if key_event.key is ESCAPE:
|
||||
if self.state <= DIFFED:
|
||||
self.quit_loop(0)
|
||||
return
|
||||
if self.state is DIFFED:
|
||||
if key_event.key is UP or key_event.key is DOWN:
|
||||
self.scroll_lines(1 if key_event.key is DOWN else -1)
|
||||
return
|
||||
if key_event.key is PAGE_UP or key_event.key is PAGE_DOWN:
|
||||
amt = self.num_lines * (1 if key_event.key is PAGE_DOWN else -1)
|
||||
self.scroll_lines(amt)
|
||||
return
|
||||
if key_event.key is HOME or key_event.key is END:
|
||||
amt = len(self.diff_lines) * (1 if key_event.key is END else -1)
|
||||
self.scroll_lines(amt)
|
||||
return
|
||||
action = self.shortcut_action(key_event)
|
||||
if action is not None:
|
||||
return self.perform_action(action)
|
||||
|
||||
def on_resize(self, screen_size):
|
||||
self.screen_size = screen_size
|
||||
@@ -414,9 +412,5 @@ def main(args):
|
||||
raise SystemExit(loop.return_code)
|
||||
|
||||
|
||||
def handle_result(args, current_char, target_window_id, boss):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
|
||||
@@ -36,8 +36,12 @@ def set_diff_command(opt):
|
||||
def run_diff(file1, file2, context=3):
|
||||
# returns: ok, is_different, patch
|
||||
cmd = shlex.split(set_diff_command.cmd.replace('_CONTEXT_', str(context)))
|
||||
# we resolve symlinks because git diff does not follow symlinks, while diff
|
||||
# does. We want consistent behavior, also for integration with git difftool
|
||||
# we always want symlinks to be followed.
|
||||
path1, path2 = map(os.path.realpath, (file1, file2))
|
||||
p = subprocess.Popen(
|
||||
cmd + [file1, file2],
|
||||
cmd + [path1, path2],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.DEVNULL)
|
||||
stdout, stderr = p.communicate()
|
||||
returncode = p.wait()
|
||||
|
||||
@@ -173,12 +173,16 @@ def url(text, s, e):
|
||||
e -= len(url) - idx
|
||||
while text[e - 1] in '.,?!' and e > 1: # remove trailing punctuation
|
||||
e -= 1
|
||||
# Restructured Text URLs
|
||||
if e > 3 and text[e-2:e] == '`_':
|
||||
e -= 2
|
||||
# Remove trailing bracket if matched by leading bracket
|
||||
if s > 0 and e < len(text) and text[s-1] in opening_brackets and text[e-1] == closing_bracket_map[text[s-1]]:
|
||||
e -= 1
|
||||
# Remove trailing quote if matched by leading quote
|
||||
if s > 0 and e < len(text) and text[s-1] in '\'"' and text[e-1] == text[s-1]:
|
||||
e -= 1
|
||||
|
||||
return s, e
|
||||
|
||||
|
||||
|
||||
@@ -304,9 +304,5 @@ def main(args=sys.argv):
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def handle_result(args, current_char, target_window_id, boss):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -126,10 +126,6 @@ def main(args):
|
||||
handler = Resize(args)
|
||||
loop.loop(handler)
|
||||
if handler.print_on_fail:
|
||||
print(handler.print_on_fail)
|
||||
print(handler.print_on_fail, file=sys.stderr)
|
||||
input('Press Enter to quit')
|
||||
raise SystemExit(loop.return_code)
|
||||
|
||||
|
||||
def handle_result(args, data, target_window_id, boss):
|
||||
pass
|
||||
|
||||
@@ -28,11 +28,12 @@ def import_kitten_main_module(config_dir, kitten):
|
||||
code = compile(src, path, 'exec')
|
||||
g = {'__name__': 'kitten'}
|
||||
exec(code, g)
|
||||
return {'start': g['main'], 'end': g['handle_result']}
|
||||
hr = g.get('handle_result', lambda *a, **kw: None)
|
||||
return {'start': g['main'], 'end': hr}
|
||||
else:
|
||||
kitten = resolved_kitten(kitten)
|
||||
m = importlib.import_module('kittens.{}.main'.format(kitten))
|
||||
return {'start': m.main, 'end': m.handle_result}
|
||||
return {'start': m.main, 'end': getattr(m, 'handle_result', lambda *a, **k: None)}
|
||||
|
||||
|
||||
def create_kitten_handler(kitten, orig_args):
|
||||
|
||||
0
kittens/show_error/__init__.py
Normal file
0
kittens/show_error/__init__.py
Normal file
42
kittens/show_error/main.py
Normal file
42
kittens/show_error/main.py
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import sys
|
||||
|
||||
from kitty.cli import parse_args
|
||||
from ..tui.operations import styled
|
||||
|
||||
|
||||
OPTIONS = '''\
|
||||
--title
|
||||
default=ERROR
|
||||
The title for the error message.
|
||||
'''.format
|
||||
|
||||
|
||||
def real_main(args):
|
||||
error_message = sys.stdin.buffer.read().decode('utf-8')
|
||||
sys.stdin = open('/dev/tty')
|
||||
msg = 'Show an error message'
|
||||
args, items = parse_args(args, OPTIONS, '', msg, 'hints')
|
||||
print(styled(args.title, fg_intense=True, fg='red', bold=True))
|
||||
print()
|
||||
print(error_message)
|
||||
print()
|
||||
input('Press Enter to close.')
|
||||
|
||||
|
||||
def main(args):
|
||||
try:
|
||||
real_main(args)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
input('Press Enter to close.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
0
kittens/ssh/__init__.py
Normal file
0
kittens/ssh/__init__.py
Normal file
51
kittens/ssh/main.py
Normal file
51
kittens/ssh/main.py
Normal file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
SHELL_SCRIPT = '''\
|
||||
#!/bin/sh
|
||||
# macOS ships with an ancient version of tic that cannot read from stdin, so we
|
||||
# create a temp file for it
|
||||
tmp=$(mktemp /tmp/terminfo.XXXXXX)
|
||||
cat >$tmp << 'TERMEOF'
|
||||
TERMINFO
|
||||
TERMEOF
|
||||
|
||||
tic_out=$(tic -x -o ~/.terminfo $tmp 2>&1)
|
||||
rc=$?
|
||||
rm $tmp
|
||||
if [ "$rc" != "0" ]; then echo "$tic_out"; exit 1; fi
|
||||
if [ -z "$USER" ]; then export USER=$(whoami); fi
|
||||
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
|
||||
|
||||
case "dash" in
|
||||
*$shell_name*)
|
||||
python=$(command -v python3)
|
||||
if [ -z "$python" ]; then python=$(command -v python2); fi
|
||||
if [ -z "$python" ]; then python=python; fi
|
||||
exec $python -c "import os; os.execlp('$0', '-' '$shell_name')"
|
||||
;;
|
||||
esac
|
||||
exec -a "-$shell_name" "$0"
|
||||
'''
|
||||
|
||||
|
||||
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())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
@@ -18,6 +18,19 @@ class Handler:
|
||||
self.cmd = commander(self)
|
||||
self.image_manager = image_manager
|
||||
|
||||
def add_shortcut(self, action, key, mods=None, is_text=False):
|
||||
if not hasattr(self, '_text_shortcuts'):
|
||||
self._text_shortcuts, self._key_shortcuts = {}, {}
|
||||
if is_text:
|
||||
self._text_shortcuts[key] = action
|
||||
else:
|
||||
self._key_shortcuts[(key, mods or 0)] = action
|
||||
|
||||
def shortcut_action(self, key_event_or_text):
|
||||
if isinstance(key_event_or_text, str):
|
||||
return self._text_shortcuts.get(key_event_or_text)
|
||||
return self._key_shortcuts.get((key_event_or_text.key, key_event_or_text.mods))
|
||||
|
||||
def __enter__(self):
|
||||
if self.image_manager is not None:
|
||||
self.image_manager.__enter__()
|
||||
@@ -67,6 +80,9 @@ class Handler:
|
||||
def on_kitty_cmd_response(self, response):
|
||||
pass
|
||||
|
||||
def on_clipboard_response(self, text, from_primary=False):
|
||||
pass
|
||||
|
||||
def write(self, data):
|
||||
if isinstance(data, str):
|
||||
data = data.encode('utf-8')
|
||||
|
||||
@@ -170,7 +170,7 @@ class Loop:
|
||||
self.read_buf = ''
|
||||
self.decoder = codecs.getincrementaldecoder('utf-8')('ignore')
|
||||
try:
|
||||
self.iov_limit = os.sysconf('SC_IOV_MAX') - 1
|
||||
self.iov_limit = max(os.sysconf('SC_IOV_MAX') - 1, 255)
|
||||
except Exception:
|
||||
self.iov_limit = 255
|
||||
self.parse_input_from_terminal = partial(parse_input_from_terminal, self._on_text, self._on_dcs, self._on_csi, self._on_osc, self._on_pm, self._on_apc)
|
||||
@@ -265,7 +265,15 @@ class Loop:
|
||||
pass
|
||||
|
||||
def _on_osc(self, osc):
|
||||
pass
|
||||
m = re.match(r'(\d+);', osc)
|
||||
if m is not None:
|
||||
code = int(m.group(1))
|
||||
rest = osc[m.end():]
|
||||
if code == 52:
|
||||
where, rest = rest.partition(';')[::2]
|
||||
from_primary = 'p' in where
|
||||
from base64 import standard_b64decode
|
||||
self.handler.on_clipboard_response(standard_b64decode(rest).decode('utf-8'), from_primary)
|
||||
|
||||
def _on_apc(self, apc):
|
||||
if apc.startswith('K'):
|
||||
|
||||
@@ -231,6 +231,17 @@ def set_default_colors(fg=None, bg=None) -> str:
|
||||
return ans
|
||||
|
||||
|
||||
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'))
|
||||
|
||||
|
||||
def request_from_clipboard(use_primary=False) -> str:
|
||||
return '\x1b]52;{};?\x07'.format('p' if use_primary else 'c')
|
||||
|
||||
|
||||
all_cmds = tuple(
|
||||
(name, obj) for name, obj in globals().items()
|
||||
if hasattr(obj, '__annotations__') and obj.__annotations__.get('return') is str)
|
||||
|
||||
@@ -19,11 +19,12 @@ from .constants import (
|
||||
appname, config_dir, editor, set_boss, supports_primary_selection
|
||||
)
|
||||
from .fast_data_types import (
|
||||
ChildMonitor, 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
|
||||
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
|
||||
)
|
||||
from .fonts.render import prerender, resize_fonts, set_font_family
|
||||
from .keys import get_shortcut, shortcut_matches
|
||||
@@ -361,6 +362,29 @@ class Boss:
|
||||
def on_dpi_change(self, os_window_id):
|
||||
self._change_font_size()
|
||||
|
||||
def _set_os_window_background_opacity(self, os_window_id, opacity):
|
||||
change_background_opacity(os_window_id, max(0.1, min(opacity, 1.0)))
|
||||
|
||||
def set_background_opacity(self, opacity):
|
||||
window = self.active_window
|
||||
if window is None or not opacity:
|
||||
return
|
||||
if not self.opts.dynamic_background_opacity:
|
||||
return self.show_error(
|
||||
_('Cannot change background opacity'),
|
||||
_('You must set the dynamic_background_opacity option in kitty.conf to be able to change background opacity'))
|
||||
os_window_id = window.os_window_id
|
||||
if opacity[0] in '+-':
|
||||
opacity = background_opacity_of(os_window_id)
|
||||
if opacity is None:
|
||||
return
|
||||
opacity += float(opacity)
|
||||
elif opacity == 'default':
|
||||
opacity = self.opts.background_opacity
|
||||
else:
|
||||
opacity = float(opacity)
|
||||
self._set_os_window_background_opacity(os_window_id, opacity)
|
||||
|
||||
@property
|
||||
def active_tab_manager(self):
|
||||
os_window_id = current_os_window()
|
||||
@@ -517,7 +541,7 @@ class Boss:
|
||||
output += str(s.linebuf.line(i))
|
||||
return output
|
||||
|
||||
def _run_kitten(self, kitten, args=()):
|
||||
def _run_kitten(self, kitten, args=(), input_data=None):
|
||||
w = self.active_window
|
||||
tab = self.active_tab
|
||||
if w is not None and tab is not None and w.overlay_for is None:
|
||||
@@ -525,17 +549,22 @@ class Boss:
|
||||
from kittens.runner import create_kitten_handler
|
||||
end_kitten = create_kitten_handler(kitten, orig_args)
|
||||
args[0:0] = [config_dir, kitten]
|
||||
type_of_input = end_kitten.type_of_input
|
||||
if type_of_input in ('text', 'history', 'ansi', 'ansi-history', 'screen', 'screen-history', 'screen-ansi', 'screen-ansi-history'):
|
||||
data = w.as_text(
|
||||
as_ansi='ansi' in type_of_input,
|
||||
add_history='history' in type_of_input,
|
||||
add_wrap_markers='screen' in type_of_input
|
||||
).encode('utf-8')
|
||||
elif type_of_input is None:
|
||||
data = None
|
||||
if input_data is None:
|
||||
type_of_input = end_kitten.type_of_input
|
||||
if type_of_input in ('text', 'history', 'ansi', 'ansi-history', 'screen', 'screen-history', 'screen-ansi', 'screen-ansi-history'):
|
||||
data = w.as_text(
|
||||
as_ansi='ansi' in type_of_input,
|
||||
add_history='history' in type_of_input,
|
||||
add_wrap_markers='screen' in type_of_input
|
||||
).encode('utf-8')
|
||||
elif type_of_input is None:
|
||||
data = None
|
||||
else:
|
||||
raise ValueError('Unknown type_of_input: {}'.format(type_of_input))
|
||||
else:
|
||||
raise ValueError('Unknown type_of_input: {}'.format(type_of_input))
|
||||
data = input_data
|
||||
if isinstance(data, str):
|
||||
data = data.encode('utf-8')
|
||||
copts = {k: self.opts[k] for k in ('select_by_word_characters', 'open_url_with')}
|
||||
overlay_window = tab.new_special_window(
|
||||
SpecialWindow(
|
||||
@@ -573,6 +602,9 @@ class Boss:
|
||||
args = ['--name=tab-title', '--message', _('Enter the new title for this tab below.'), 'do_set_tab_title', str(tab.id)]
|
||||
self._run_kitten('ask', args)
|
||||
|
||||
def show_error(self, title, msg):
|
||||
self._run_kitten('show_error', ['--title', title], input_data=msg)
|
||||
|
||||
def do_set_tab_title(self, title, tab_id):
|
||||
tm = self.active_tab_manager
|
||||
if tm is not None and title:
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#version GLSL_VERSION
|
||||
#define WHICH_PROGRAM
|
||||
#define NOT_TRANSPARENT
|
||||
#define SHIFTS
|
||||
#define DECORATION_SHIFT {DECORATION_SHIFT}
|
||||
#define REVERSE_SHIFT {REVERSE_SHIFT}
|
||||
#define STRIKE_SHIFT {STRIKE_SHIFT}
|
||||
#define DIM_SHIFT {DIM_SHIFT}
|
||||
|
||||
// Inputs {{{
|
||||
layout(std140) uniform CellRenderData {
|
||||
|
||||
@@ -614,7 +614,7 @@ static inline void
|
||||
render_os_window(OSWindow *os_window, double now, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows) {
|
||||
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);
|
||||
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);
|
||||
for (unsigned int i = 0; i < tab->num_windows; i++) {
|
||||
Window *w = tab->windows + i;
|
||||
|
||||
@@ -21,7 +21,12 @@ class MatchError(ValueError):
|
||||
ValueError.__init__(self, 'No matching {} for expression: {}'.format(target, expression))
|
||||
|
||||
|
||||
def cmd(short_desc, desc=None, options_spec=None, no_response=False, argspec='...', string_return_is_error=False):
|
||||
class OpacityError(ValueError):
|
||||
|
||||
hide_traceback = True
|
||||
|
||||
|
||||
def cmd(short_desc, desc=None, options_spec=None, no_response=False, argspec='...', string_return_is_error=False, args_count=None):
|
||||
|
||||
def w(func):
|
||||
func.short_desc = short_desc
|
||||
@@ -33,6 +38,7 @@ def cmd(short_desc, desc=None, options_spec=None, no_response=False, argspec='..
|
||||
func.impl = lambda: globals()[func.__name__[4:]]
|
||||
func.no_response = no_response
|
||||
func.string_return_is_error = string_return_is_error
|
||||
func.args_count = 0 if not argspec else args_count
|
||||
return func
|
||||
return w
|
||||
|
||||
@@ -86,7 +92,7 @@ def ls(boss, window):
|
||||
@cmd(
|
||||
'Set the font size in all windows',
|
||||
'Sets the font size to the specified size, in pts.',
|
||||
argspec='FONT_SIZE'
|
||||
argspec='FONT_SIZE', args_count=1
|
||||
)
|
||||
def cmd_set_font_size(global_opts, opts, args):
|
||||
try:
|
||||
@@ -586,11 +592,55 @@ def set_colors(boss, window, payload):
|
||||
# }}}
|
||||
|
||||
|
||||
# set_background_opacity {{{
|
||||
@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'
|
||||
' single os_window. For example: kitty @ set-background-opacity 0.5',
|
||||
options_spec='''\
|
||||
--all -a
|
||||
type=bool-set
|
||||
By default, colors are only changed for the currently active window. This option will
|
||||
cause colors to be changed in all windows.
|
||||
|
||||
''' + '\n\n' + MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t'),
|
||||
argspec='OPACITY',
|
||||
args_count=1
|
||||
)
|
||||
def cmd_set_background_opacity(global_opts, opts, args):
|
||||
opacity = max(0.1, min(float(args[0]), 1.0))
|
||||
return {
|
||||
'opacity': opacity, 'match_window': opts.match,
|
||||
'all': opts.all,
|
||||
}
|
||||
|
||||
|
||||
def set_background_opacity(boss, window, payload):
|
||||
if not boss.opts.dynamic_background_opacity:
|
||||
raise OpacityError('You must turn on the dynamic_background_opacity option in kitty.conf to be able to set background opacity')
|
||||
if payload['all']:
|
||||
windows = tuple(boss.all_windows)
|
||||
else:
|
||||
windows = (window or boss.active_window,)
|
||||
if payload['match_window']:
|
||||
windows = tuple(boss.match_windows(payload['match_window']))
|
||||
if not windows:
|
||||
raise MatchError(payload['match_window'])
|
||||
for os_window_id in {w.os_window_id for w in windows}:
|
||||
boss._set_os_window_background_opacity(os_window_id, payload['opacity'])
|
||||
# }}}
|
||||
|
||||
|
||||
cmap = {v.name: v for v in globals().values() if hasattr(v, 'is_cmd')}
|
||||
|
||||
|
||||
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))
|
||||
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))
|
||||
raise SystemExit('Must specify exactly {} argument(s) for {}'.format(func.args_count, func.name))
|
||||
return opts, items
|
||||
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ from contextlib import contextmanager
|
||||
|
||||
from . import fast_data_types as defines
|
||||
from .config_utils import (
|
||||
init_config, load_config as _load_config, merge_dicts, parse_config_base,
|
||||
positive_float, positive_int, python_string, to_bool, to_cmdline, to_color,
|
||||
unit_float
|
||||
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
|
||||
)
|
||||
from .constants import cache_dir, defconf
|
||||
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
||||
@@ -92,10 +92,61 @@ def parse_shortcut(sc):
|
||||
|
||||
|
||||
KeyAction = namedtuple('KeyAction', 'func args')
|
||||
shlex_actions = {
|
||||
func_with_args, args_funcs = key_func()
|
||||
|
||||
|
||||
@func_with_args(
|
||||
'pass_selection_to_program', 'new_window', 'new_tab', 'new_os_window',
|
||||
'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd'
|
||||
}
|
||||
)
|
||||
def shlex_parse(func, rest):
|
||||
return func, to_cmdline(rest)
|
||||
|
||||
|
||||
@func_with_args('combine')
|
||||
def combine_parse(func, rest):
|
||||
sep, rest = rest.split(' ', 1)
|
||||
parts = re.split(r'\s*' + re.escape(sep) + r'\s*', rest)
|
||||
args = tuple(map(parse_key_action, filter(None, parts)))
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('send_text')
|
||||
def send_text_parse(func, rest):
|
||||
args = rest.split(' ', 1)
|
||||
if len(args) > 0:
|
||||
try:
|
||||
args[1] = parse_send_text_bytes(args[1])
|
||||
except Exception:
|
||||
log_error('Ignoring invalid send_text string: ' + args[1])
|
||||
args[1] = ''
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('run_kitten', 'run_simple_kitten', 'kitten')
|
||||
def kitten_parse(func, rest):
|
||||
if func == 'kitten':
|
||||
args = rest.split(' ', 1)
|
||||
else:
|
||||
args = rest.split(' ', 2)[1:]
|
||||
func = 'kitten'
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('goto_tab')
|
||||
def goto_tab_parse(func, rest):
|
||||
args = (max(0, int(rest)), )
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('set_background_opacity', 'goto_layout', 'kitty_shell')
|
||||
def simple_parse(func, rest):
|
||||
return func, [rest]
|
||||
|
||||
|
||||
@func_with_args('set_font_size')
|
||||
def float_parse(func, rest):
|
||||
return func, (float(rest),)
|
||||
|
||||
|
||||
def parse_key_action(action):
|
||||
@@ -104,32 +155,12 @@ def parse_key_action(action):
|
||||
if len(parts) == 1:
|
||||
return KeyAction(func, ())
|
||||
rest = parts[1]
|
||||
if func == 'combine':
|
||||
sep, rest = rest.split(' ', 1)
|
||||
parts = re.split(r'\s*' + re.escape(sep) + r'\s*', rest)
|
||||
args = tuple(map(parse_key_action, filter(None, parts)))
|
||||
elif func == 'send_text':
|
||||
args = rest.split(' ', 1)
|
||||
if len(args) > 0:
|
||||
try:
|
||||
args[1] = parse_send_text_bytes(args[1])
|
||||
except Exception:
|
||||
log_error('Ignoring invalid send_text string: ' + args[1])
|
||||
args[1] = ''
|
||||
elif func in ('run_kitten', 'run_simple_kitten', 'kitten'):
|
||||
if func == 'kitten':
|
||||
args = rest.split(' ', 1)
|
||||
else:
|
||||
args = rest.split(' ', 2)[1:]
|
||||
func = 'kitten'
|
||||
elif func == 'goto_tab':
|
||||
args = (max(0, int(rest)), )
|
||||
elif func == 'goto_layout' or func == 'kitty_shell':
|
||||
args = [rest]
|
||||
elif func == 'set_font_size':
|
||||
args = (float(rest),)
|
||||
elif func in shlex_actions:
|
||||
args = to_cmdline(rest)
|
||||
parser = args_funcs.get(func)
|
||||
if parser is not None:
|
||||
try:
|
||||
func, args = parser(func, rest)
|
||||
except Exception:
|
||||
log_error('Ignoring invalid key action: {}'.format(action))
|
||||
return KeyAction(func, args)
|
||||
|
||||
|
||||
@@ -354,6 +385,7 @@ type_map = {
|
||||
'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,
|
||||
|
||||
@@ -170,3 +170,73 @@ def init_config(defaults_path, parse_config):
|
||||
Options = create_options_class(defaults.keys())
|
||||
defaults = Options(defaults)
|
||||
return Options, defaults
|
||||
|
||||
|
||||
def key_func():
|
||||
ans = {}
|
||||
|
||||
def func_with_args(*names):
|
||||
|
||||
def w(f):
|
||||
for name in names:
|
||||
if ans.setdefault(name, f) is not f:
|
||||
raise ValueError('the args_func {} is being redefined'.format(name))
|
||||
return f
|
||||
|
||||
return w
|
||||
return func_with_args, ans
|
||||
|
||||
|
||||
def parse_kittens_shortcut(sc):
|
||||
from kitty.key_encoding import config_key_map, config_mod_map, text_match
|
||||
if sc.endswith('+'):
|
||||
parts = list(filter(None, sc.rstrip('+').split('+') + ['+']))
|
||||
else:
|
||||
parts = sc.split('+')
|
||||
mods = parts[:-1] or None
|
||||
if mods is not None:
|
||||
resolved_mods = 0
|
||||
for mod in mods:
|
||||
m = config_mod_map.get(mod.upper())
|
||||
if m is None:
|
||||
raise ValueError('Unknown shortcut modifiers: {}'.format(sc))
|
||||
resolved_mods |= m
|
||||
mods = resolved_mods
|
||||
is_text = False
|
||||
rkey = parts[-1]
|
||||
tkey = text_match(rkey)
|
||||
if tkey is None:
|
||||
rkey = rkey.upper()
|
||||
rkey = config_key_map.get(rkey)
|
||||
if rkey is None:
|
||||
raise ValueError('Unknown shortcut key: {}'.format(sc))
|
||||
else:
|
||||
is_text = True
|
||||
rkey = tkey
|
||||
return mods, rkey, is_text
|
||||
|
||||
|
||||
def parse_kittens_func_args(action, args_funcs):
|
||||
parts = action.split(' ', 1)
|
||||
func = parts[0]
|
||||
if len(parts) == 1:
|
||||
return func, ()
|
||||
rest = parts[1]
|
||||
parser = args_funcs.get(func)
|
||||
if parser is not None:
|
||||
try:
|
||||
func, args = parser(func, rest)
|
||||
except Exception:
|
||||
raise ValueError('Unknown key action: {}'.format(action))
|
||||
if not isinstance(args, (list, tuple)):
|
||||
args = (args,)
|
||||
return func, tuple(args)
|
||||
|
||||
|
||||
def parse_kittens_key(val, funcs_with_args):
|
||||
sc, action = val.partition(' ')[::2]
|
||||
if not sc or not action:
|
||||
return
|
||||
mods, key, is_text = parse_kittens_shortcut(sc)
|
||||
action = parse_kittens_func_args(action, funcs_with_args)
|
||||
return action, key, mods, is_text
|
||||
|
||||
@@ -11,7 +11,7 @@ from collections import namedtuple
|
||||
from .fast_data_types import set_boss as set_c_boss
|
||||
|
||||
appname = 'kitty'
|
||||
version = (0, 10, 0)
|
||||
version = (0, 10, 1)
|
||||
str_version = '.'.join(map(str, version))
|
||||
_plat = sys.platform.lower()
|
||||
is_macos = 'darwin' in _plat
|
||||
|
||||
@@ -365,7 +365,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
PyErr_SetString(PyExc_ValueError, "Too many windows");
|
||||
return NULL;
|
||||
}
|
||||
bool want_semi_transparent = (1.0 - OPT(background_opacity) >= 0.01) ? true : false;
|
||||
bool want_semi_transparent = (1.0 - OPT(background_opacity) >= 0.01) || OPT(dynamic_background_opacity);
|
||||
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, want_semi_transparent);
|
||||
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, global_state.num_os_windows ? global_state.os_windows[0].handle : NULL);
|
||||
if (glfw_window == NULL) {
|
||||
@@ -385,12 +385,14 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
|
||||
Py_DECREF(ret);
|
||||
#ifdef __APPLE__
|
||||
cocoa_create_global_menu();
|
||||
if (OPT(macos_option_as_alt)) glfwSetCocoaTextInputFilter(glfw_window, filter_option);
|
||||
// This needs to be done only after the first window has been created, because glfw only sets the activation policy once upon initialization.
|
||||
if (OPT(macos_hide_from_tasks)) cocoa_set_hide_from_tasks();
|
||||
#endif
|
||||
is_first_window = false;
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
if (OPT(macos_option_as_alt)) glfwSetCocoaTextInputFilter(glfw_window, filter_option);
|
||||
#endif
|
||||
|
||||
OSWindow *w = add_os_window();
|
||||
w->handle = glfw_window;
|
||||
|
||||
38
kitty/key_encoding.py
generated
38
kitty/key_encoding.py
generated
@@ -254,6 +254,17 @@ KEY_MAP = {
|
||||
# END_ENCODING }}}
|
||||
|
||||
|
||||
text_keys = string.ascii_uppercase + string.ascii_lowercase + string.digits + '`~!@#$%^&*()_-+=[{]}\\|<,>./?;:\'" '
|
||||
|
||||
|
||||
def text_match(key):
|
||||
if key.upper() == 'SPACE':
|
||||
return ' '
|
||||
if key not in text_keys:
|
||||
return
|
||||
return key
|
||||
|
||||
|
||||
def encode(
|
||||
integer,
|
||||
chars=string.ascii_uppercase + string.ascii_lowercase + string.digits +
|
||||
@@ -312,13 +323,34 @@ type_map = {'p': PRESS, 't': REPEAT, 'r': RELEASE}
|
||||
mod_map = {c: i for i, c in enumerate('ABCDEFGHIJKLMNOP')}
|
||||
key_rmap = {}
|
||||
g = globals()
|
||||
config_key_map = {}
|
||||
config_mod_map = {'SHIFT': SHIFT, 'ALT': ALT, 'OPTION': ALT, '⌥': ALT, '⌘': SUPER, 'CMD': SUPER, 'SUPER': SUPER, 'CTRL': CTRL, 'CONTROL': CTRL}
|
||||
for key_name, enc in ENCODING.items():
|
||||
key_name = key_name.replace(' ', '_')
|
||||
g[key_name] = key_name
|
||||
g[key_name] = config_key_map[key_name] = key_name
|
||||
key_rmap[enc] = key_name
|
||||
config_key_map.update({
|
||||
'`': g['GRAVE_ACCENT'],
|
||||
'-': g['MINUS'],
|
||||
'=': g['EQUAL'],
|
||||
|
||||
'[': g['LEFT_BRACKET'],
|
||||
']': g['RIGHT_BRACKET'],
|
||||
'\\': g['BACKSLASH'],
|
||||
|
||||
';': g['SEMICOLON'],
|
||||
"'": g['APOSTROPHE'],
|
||||
|
||||
',': g['COMMA'],
|
||||
'.': g['PERIOD'],
|
||||
'/': g['SLASH'],
|
||||
|
||||
'ESC': g['ESCAPE'],
|
||||
})
|
||||
|
||||
enter_key = KeyEvent(PRESS, 0, g['ENTER'])
|
||||
backspace_key = KeyEvent(PRESS, 0, g['BACKSPACE'])
|
||||
del key_name, enc, g
|
||||
enter_key = KeyEvent(PRESS, 0, ENCODING['ENTER'])
|
||||
backspace_key = KeyEvent(PRESS, 0, ENCODING['BACKSPACE'])
|
||||
|
||||
|
||||
def decode_key_event(text):
|
||||
|
||||
8
kitty/keys.h
generated
8
kitty/keys.h
generated
@@ -1230,6 +1230,8 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
||||
} // end switch(key)
|
||||
case 0x6:
|
||||
switch(key & 0x7f) { default: return NULL;
|
||||
case 0: // SPACE
|
||||
return "\x02\x1b\x00";
|
||||
case 18: // A
|
||||
return "\x02\x1b\x01";
|
||||
case 19: // B
|
||||
@@ -2853,6 +2855,8 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
||||
} // end switch(key)
|
||||
case 0x6:
|
||||
switch(key & 0x7f) { default: return NULL;
|
||||
case 0: // SPACE
|
||||
return "\x02\x1b\x00";
|
||||
case 18: // A
|
||||
return "\x02\x1b\x01";
|
||||
case 19: // B
|
||||
@@ -4485,6 +4489,8 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
||||
} // end switch(key)
|
||||
case 0x6:
|
||||
switch(key & 0x7f) { default: return NULL;
|
||||
case 0: // SPACE
|
||||
return "\x02\x1b\x00";
|
||||
case 18: // A
|
||||
return "\x02\x1b\x01";
|
||||
case 19: // B
|
||||
@@ -6108,6 +6114,8 @@ key_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {
|
||||
} // end switch(key)
|
||||
case 0x6:
|
||||
switch(key & 0x7f) { default: return NULL;
|
||||
case 0: // SPACE
|
||||
return "\x02\x1b\x00";
|
||||
case 18: // A
|
||||
return "\x02\x1b\x01";
|
||||
case 19: // B
|
||||
|
||||
@@ -49,7 +49,9 @@ SHIFTED_KEYS = {
|
||||
defines.GLFW_KEY_DOWN: key_as_bytes('kind'),
|
||||
}
|
||||
|
||||
control_alt_codes = {}
|
||||
control_alt_codes = {
|
||||
defines.GLFW_KEY_SPACE: b'\x1b\0',
|
||||
}
|
||||
|
||||
for kf, kn in {
|
||||
defines.GLFW_KEY_UP: 'kcuu1',
|
||||
|
||||
@@ -258,6 +258,9 @@ background #000000
|
||||
# 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.
|
||||
background_opacity 1.0
|
||||
# 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)
|
||||
dynamic_background_opacity no
|
||||
|
||||
# How much to dim text that has the DIM/FAINT attribute set. 1.0 means no dimming and
|
||||
# 0.0 means fully dimmed (i.e. invisible).
|
||||
@@ -498,6 +501,10 @@ map kitty_mod+u input_unicode_character
|
||||
map kitty_mod+f2 edit_config_file
|
||||
# Open the kitty shell in a new window/tab/overlay/os_window to control kitty using commands.
|
||||
map kitty_mod+escape kitty_shell window
|
||||
map kitty_mod+a>m set_background_opacity +0.1
|
||||
map kitty_mod+a>l set_background_opacity -0.1
|
||||
map kitty_mod+a>1 set_background_opacity 1
|
||||
map kitty_mod+a>d set_background_opacity default
|
||||
|
||||
# Sending arbitrary text on shortcut key presses
|
||||
# You can tell kitty to send arbitrary (UTF-8) encoded text to
|
||||
|
||||
@@ -209,7 +209,7 @@ struct CellUniformData {
|
||||
static struct CellUniformData cell_uniform_data = {0, .prev_inactive_text_alpha=-1};
|
||||
|
||||
static inline void
|
||||
cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, CursorRenderInfo *cursor, bool inverted) {
|
||||
cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, CursorRenderInfo *cursor, bool inverted, OSWindow *os_window) {
|
||||
struct CellRenderData {
|
||||
GLfloat xstart, ystart, dx, dy, sprite_dx, sprite_dy, background_opacity;
|
||||
|
||||
@@ -239,7 +239,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G
|
||||
sprite_tracker_current_layout(&x, &y, &z);
|
||||
rd->sprite_dx = 1.0f / (float)x; rd->sprite_dy = 1.0f / (float)y;
|
||||
rd->inverted = inverted ? 1 : 0;
|
||||
rd->background_opacity = OPT(background_opacity);
|
||||
rd->background_opacity = os_window->background_opacity;
|
||||
|
||||
#define COLOR(name) colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name)
|
||||
rd->default_fg = COLOR(default_fg); rd->default_bg = COLOR(default_bg); rd->highlight_fg = COLOR(highlight_fg); rd->highlight_bg = COLOR(highlight_bg);
|
||||
@@ -415,7 +415,7 @@ send_cell_data_to_gpu(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat
|
||||
if (os_window->clear_count < 2) {
|
||||
os_window->clear_count++;
|
||||
#define C(shift) (((GLfloat)((OPT(background) >> shift) & 0xFF)) / 255.0f)
|
||||
glClearColor(C(16), C(8), C(0), os_window->is_semi_transparent ? OPT(background_opacity) : 1.0f);
|
||||
glClearColor(C(16), C(8), C(0), os_window->is_semi_transparent ? os_window->background_opacity : 1.0f);
|
||||
#undef C
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
changed = true;
|
||||
@@ -430,7 +430,7 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GL
|
||||
CELL_BUFFERS;
|
||||
bool inverted = screen_invert_colors(screen);
|
||||
|
||||
cell_update_uniform_block(vao_idx, screen, uniform_buffer, xstart, ystart, dx, dy, &screen->cursor_render_info, inverted);
|
||||
cell_update_uniform_block(vao_idx, screen, uniform_buffer, xstart, ystart, dx, dy, &screen->cursor_render_info, inverted, os_window);
|
||||
|
||||
bind_vao_uniform_buffer(vao_idx, uniform_buffer, cell_program_layouts[CELL_PROGRAM].render_data.index);
|
||||
bind_vertex_array(vao_idx);
|
||||
@@ -522,7 +522,7 @@ create_border_vao() {
|
||||
}
|
||||
|
||||
void
|
||||
draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type active_window_bg, unsigned int num_visible_windows) {
|
||||
draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type active_window_bg, unsigned int num_visible_windows, OSWindow *w) {
|
||||
if (num_border_rects) {
|
||||
if (rect_data_is_dirty) {
|
||||
size_t sz = sizeof(GLuint) * 5 * num_border_rects;
|
||||
@@ -535,11 +535,14 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
|
||||
#define CV3(x) (((float)((x >> 16) & 0xff))/255.f), (((float)((x >> 8) & 0xff))/255.f), (((float)(x & 0xff))/255.f)
|
||||
if (!constants_set) {
|
||||
constants_set = true;
|
||||
glUniform1f(border_uniform_locations[BORDER_background_opacity], OPT(background_opacity));
|
||||
glUniform1f(border_uniform_locations[BORDER_background_opacity], w->background_opacity);
|
||||
glUniform3f(border_uniform_locations[BORDER_active_border_color], CV3(OPT(active_border_color)));
|
||||
glUniform3f(border_uniform_locations[BORDER_inactive_border_color], CV3(OPT(inactive_border_color)));
|
||||
glUniform3f(border_uniform_locations[BORDER_bell_border_color], CV3(OPT(bell_border_color)));
|
||||
}
|
||||
if (OPT(dynamic_background_opacity)) {
|
||||
glUniform1f(border_uniform_locations[BORDER_background_opacity], w->background_opacity);
|
||||
}
|
||||
glUniform2ui(border_uniform_locations[BORDER_viewport], viewport_width, viewport_height);
|
||||
color_type default_bg = num_visible_windows > 1 ? OPT(background) : active_window_bg;
|
||||
glUniform3f(border_uniform_locations[BORDER_default_bg], CV3(default_bg));
|
||||
|
||||
@@ -81,6 +81,7 @@ add_os_window() {
|
||||
memset(ans, 0, sizeof(OSWindow));
|
||||
ans->id = ++global_state.os_window_id_counter;
|
||||
ans->tab_bar_render_data.vao_idx = create_cell_vao();
|
||||
ans->background_opacity = OPT(background_opacity);
|
||||
END_WITH_OS_WINDOW_REFS
|
||||
return ans;
|
||||
}
|
||||
@@ -360,6 +361,7 @@ PYWRAP1(set_options) {
|
||||
S(cursor_stop_blinking_after, PyFloat_AsDouble);
|
||||
S(background_opacity, PyFloat_AsDouble);
|
||||
S(dim_opacity, PyFloat_AsDouble);
|
||||
S(dynamic_background_opacity, PyObject_IsTrue);
|
||||
S(inactive_text_alpha, PyFloat_AsDouble);
|
||||
S(window_padding_width, PyFloat_AsDouble);
|
||||
S(cursor_shape, PyLong_AsLong);
|
||||
@@ -503,6 +505,26 @@ PYWRAP1(mark_tab_bar_dirty) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PYWRAP1(change_background_opacity) {
|
||||
id_type os_window_id;
|
||||
float opacity;
|
||||
PA("Kf", &os_window_id, &opacity);
|
||||
WITH_OS_WINDOW(os_window_id)
|
||||
os_window->background_opacity = opacity;
|
||||
os_window->is_damaged = true;
|
||||
Py_RETURN_TRUE;
|
||||
END_WITH_OS_WINDOW
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
PYWRAP1(background_opacity_of) {
|
||||
id_type os_window_id = PyLong_AsUnsignedLongLong(args);
|
||||
WITH_OS_WINDOW(os_window_id)
|
||||
return PyFloat_FromDouble((double)os_window->background_opacity);
|
||||
END_WITH_OS_WINDOW
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
fix_window_idx(Tab *tab, id_type window_id, unsigned int *window_idx) {
|
||||
for (id_type fix = 0; fix < tab->num_windows; fix++) {
|
||||
@@ -630,6 +652,8 @@ static PyMethodDef module_methods[] = {
|
||||
MW(mark_os_window_for_close, METH_VARARGS),
|
||||
MW(set_titlebar_color, METH_VARARGS),
|
||||
MW(mark_tab_bar_dirty, METH_O),
|
||||
MW(change_background_opacity, METH_VARARGS),
|
||||
MW(background_opacity_of, METH_O),
|
||||
MW(update_window_visibility, METH_VARARGS),
|
||||
MW(set_boss, METH_O),
|
||||
MW(set_display_state, METH_VARARGS),
|
||||
|
||||
@@ -27,6 +27,7 @@ typedef struct {
|
||||
int adjust_line_height_px, adjust_column_width_px;
|
||||
float adjust_line_height_frac, adjust_column_width_frac;
|
||||
float background_opacity, dim_opacity;
|
||||
bool dynamic_background_opacity;
|
||||
float inactive_text_alpha;
|
||||
float window_padding_width;
|
||||
Edge tab_bar_edge;
|
||||
@@ -118,6 +119,7 @@ typedef struct {
|
||||
uint32_t offscreen_texture_id;
|
||||
unsigned int clear_count;
|
||||
color_type last_titlebar_color;
|
||||
float background_opacity;
|
||||
} OSWindow;
|
||||
|
||||
|
||||
@@ -171,7 +173,7 @@ OSWindow* add_os_window();
|
||||
OSWindow* current_os_window();
|
||||
void os_window_regions(OSWindow*, Region *main, Region *tab_bar);
|
||||
bool drag_scroll(Window *, OSWindow*);
|
||||
void draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type, unsigned int);
|
||||
void draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type, unsigned int, OSWindow *w);
|
||||
ssize_t create_cell_vao();
|
||||
ssize_t create_graphics_vao();
|
||||
ssize_t create_border_vao();
|
||||
|
||||
@@ -335,7 +335,7 @@ def make_fd_non_blocking(fd):
|
||||
|
||||
|
||||
@contextmanager
|
||||
def non_blocking_read(src=sys.stdin):
|
||||
def non_blocking_read(src=sys.stdin, disable_echo=False):
|
||||
import termios
|
||||
import tty
|
||||
import fcntl
|
||||
@@ -343,6 +343,10 @@ def non_blocking_read(src=sys.stdin):
|
||||
if src.isatty():
|
||||
old = termios.tcgetattr(fd)
|
||||
tty.setraw(fd)
|
||||
if disable_echo:
|
||||
new = list(old)
|
||||
new[3] |= termios.ECHO
|
||||
termios.tcsetattr(fd, termios.TCSANOW, new)
|
||||
oldfl = make_fd_non_blocking(fd)
|
||||
yield fd
|
||||
if src.isatty():
|
||||
|
||||
@@ -65,10 +65,8 @@ def load_shader_programs(semi_transparent=0):
|
||||
'FOREGROUND': CELL_FG_PROGRAM,
|
||||
}.items():
|
||||
vv, ff = v.replace('WHICH_PROGRAM', which), f.replace('WHICH_PROGRAM', which)
|
||||
shifts = '\n'.join('#define {} {}'.format(name, val) for name, val in (
|
||||
('DECORATION_SHIFT', DECORATION), ('REVERSE_SHIFT', REVERSE), ('STRIKE_SHIFT', STRIKETHROUGH), ('DIM_SHIFT', DIM),
|
||||
))
|
||||
vv = vv.replace('#define SHIFTS', shifts)
|
||||
for gln, pyn in {'REVERSE_SHIFT': REVERSE, 'STRIKE_SHIFT': STRIKETHROUGH, 'DIM_SHIFT': DIM, 'DECORATION_SHIFT': DECORATION}.items():
|
||||
vv = vv.replace('{{{}}}'.format(gln), str(pyn), 1)
|
||||
if semi_transparent:
|
||||
vv = vv.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')
|
||||
ff = ff.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')
|
||||
@@ -346,16 +344,16 @@ class Window:
|
||||
where, text = data.partition(';')[::2]
|
||||
if text == '?':
|
||||
response = None
|
||||
if 'read-clipboard' in self.opts.clipboard_control and ('s' in where or 'c' in where):
|
||||
response = get_clipboard_string()
|
||||
if 's' in where or 'c' in where:
|
||||
response = get_clipboard_string() if 'read-clipboard' in self.opts.clipboard_control else ''
|
||||
loc = 'c'
|
||||
elif 'p' in where and 'read-primary' in self.opts.clipboard_control:
|
||||
response = get_primary_selection()
|
||||
elif 'p' in where:
|
||||
response = get_primary_selection() if 'read-primary' in self.opts.clipboard_control else ''
|
||||
loc = 'p'
|
||||
if response is not None:
|
||||
from base64 import standard_b64encode
|
||||
self.screen.send_escape_code_to_child(OSC, '{};{}'.format(
|
||||
loc, standard_b64encode(response.encode('utf-8')).decode('ascii')))
|
||||
response = response or ''
|
||||
from base64 import standard_b64encode
|
||||
self.screen.send_escape_code_to_child(OSC, '52;{};{}'.format(
|
||||
loc, standard_b64encode(response.encode('utf-8')).decode('ascii')))
|
||||
|
||||
else:
|
||||
from base64 import standard_b64decode
|
||||
@@ -365,7 +363,7 @@ class Window:
|
||||
log_error('Invalid data to write to clipboard received, ignoring')
|
||||
return
|
||||
if 's' in where or 'c' in where:
|
||||
if 'write-clipboard' in self.opts.clipboard_control:
|
||||
if 'write-clipboard' not in self.opts.clipboard_control:
|
||||
set_clipboard_string(text)
|
||||
if 'p' in where:
|
||||
if self.opts.copy_on_select:
|
||||
|
||||
@@ -28,3 +28,4 @@ class TestHints(BaseTest):
|
||||
t('({})'.format(u), u)
|
||||
t(u + '\nxxx', u + 'xxx', len(u))
|
||||
t('link:{}[xxx]'.format(u), u)
|
||||
t('`xyz <{}>`_.'.format(u), u)
|
||||
|
||||
@@ -17,7 +17,7 @@ for line in open('kitty/kitty.conf'):
|
||||
if line.startswith('map '):
|
||||
_, sc, name = line.split(maxsplit=2)
|
||||
sc = sc.replace('kitty_mod', 'ctrl+shift')
|
||||
name = name.rstrip().replace(' ', '_').replace('-', '_').replace('___', '_').replace('__', '_').strip('_')
|
||||
name = name.rstrip().replace(' ', '_').replace('-', '_').replace('+', 'plus').replace('.', '_').replace('___', '_').replace('__', '_').strip('_')
|
||||
defns[name].append('`' + sc.replace('>', ' → ') + '`')
|
||||
|
||||
defns = [
|
||||
|
||||
Reference in New Issue
Block a user