mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-14 20:47:58 +02:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95245ee473 | ||
|
|
40bc1d4f38 | ||
|
|
808c68dd0b | ||
|
|
c26b441105 | ||
|
|
036241fc6c | ||
|
|
0c1d239b5f | ||
|
|
e51fda041e | ||
|
|
4315bcfdc9 | ||
|
|
86797ff96d | ||
|
|
173bf4a4a3 | ||
|
|
807e14684f | ||
|
|
d75d372da0 | ||
|
|
2ac26b0e6d | ||
|
|
6d06415ec5 | ||
|
|
8dc1dfed48 | ||
|
|
3b27381a9b | ||
|
|
3abf808843 | ||
|
|
e74188fcf0 | ||
|
|
9708959438 | ||
|
|
4ed01bdd1e | ||
|
|
b3b2f56890 | ||
|
|
7c4977146f | ||
|
|
9ba34941e5 | ||
|
|
d0ac1c62fe | ||
|
|
0084f93e1b | ||
|
|
33fa9a08e4 | ||
|
|
80ec6ae4b6 | ||
|
|
6fabb47aa2 | ||
|
|
eab801cd17 | ||
|
|
79983897b5 | ||
|
|
91273276b5 | ||
|
|
8a98b3f4ef | ||
|
|
519b4e43a2 | ||
|
|
554a2b1811 | ||
|
|
aa83860628 |
@@ -40,7 +40,7 @@ Action Shortcut
|
||||
======================== =======================
|
||||
New tab :sc:`new_tab` (also :kbd:`⌘+t` on macOS)
|
||||
Close tab :sc:`close_tab` (also :kbd:`⌘+w` on macOS)
|
||||
Next tab :sc:`next_tab` (also :kbd:`⌃+⇥` and :kbd:`⇧+⌘+]` on macOS)
|
||||
Next tab :sc:`next_tab` (also :kbd:`⇧+⌃+⇥` and :kbd:`⇧+⌘+]` on macOS)
|
||||
Previous tab :sc:`previous_tab` (also :kbd:`⇧+⌃+⇥` and :kbd:`⇧+⌘+[` on macOS)
|
||||
Next layout :sc:`next_layout`
|
||||
Move tab forward :sc:`move_tab_forward`
|
||||
|
||||
@@ -99,12 +99,12 @@ Customizing the installation
|
||||
_kitty_install_cmd \
|
||||
installer=nightly dest=/some/other/location
|
||||
|
||||
* You can specify a different install location, with ``dest``:
|
||||
* You can specify a specific version to install, with:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
_kitty_install_cmd \
|
||||
dest=/some/other/location
|
||||
installer=version-0.35.2
|
||||
|
||||
* You can tell the installer not to launch |kitty| after installing it with
|
||||
``launch=n``:
|
||||
|
||||
@@ -74,6 +74,19 @@ consumption to do the same tasks.
|
||||
Detailed list of changes
|
||||
-------------------------------------
|
||||
|
||||
0.36.1 [2024-08-24]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Allow specifying that the :opt:`cursor shape for unfocused windows <cursor_shape_unfocused>` should remain unchanged (:pull:`7728`)
|
||||
|
||||
- MacOS Intel: Fix a crash in the choose-fonts kitten when displaying previews of variable fonts (:iss:`7734`)
|
||||
|
||||
- Remote control: Fix a regression causing an escape code to leak when using @ launch with ``--no-response`` over the TTY (:iss:`7752`)
|
||||
|
||||
- OSC 52: Fix a regression in the previous release that broke handling of invalid base64 encoded data in OSC 52 requests (:iss:`7757`)
|
||||
|
||||
- macOS: Fix a regression in the previous release that caused :option:`kitty --single-instance` to not work when using :file:`macos-launch-services-cmdline`
|
||||
|
||||
0.36.0 [2024-08-17]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -141,22 +141,24 @@ compatibility, but a sane, rigorously specified subset is chosen.
|
||||
|
||||
RGB colors are encoded in one of three forms:
|
||||
|
||||
rgb:<red>/<green>/<blue>
|
||||
<red>, <green>, <blue> := h | hh | hhh | hhhh
|
||||
h := single hexadecimal digits (case insignificant)
|
||||
Note that h indicates the value scaled in 4 bits, hh the value scaled in 8 bits, hhh the value scaled in 12 bits, and hhhh the value scaled
|
||||
in 16 bits, respectively.
|
||||
``rgb:<red>/<green>/<blue>``
|
||||
| <red>, <green>, <blue> := h | hh | hhh | hhhh
|
||||
| h := single hexadecimal digits (case insignificant)
|
||||
| Note that h indicates the value scaled in 4 bits, hh the value scaled in 8 bits,
|
||||
hhh the value scaled in 12 bits, and hhhh the value scaled in 16 bits, respectively.
|
||||
|
||||
#<h...>
|
||||
h := single hexadecimal digits (case insignificant)
|
||||
#RGB (4 bits each)
|
||||
#RRGGBB (8 bits each)
|
||||
#RRRGGGBBB (12 bits each)
|
||||
#RRRRGGGGBBBB (16 bits each)
|
||||
The R, G, and B represent single hexadecimal digits. When fewer than 16 bits each are specified, they represent the most significant bits
|
||||
of the value (unlike the “rgb:” syntax, in which values are scaled). For example, the string ``#3a7`` is the same as ``#3000a0007000``.
|
||||
``#<h...>``
|
||||
| h := single hexadecimal digits (case insignificant)
|
||||
| #RGB (4 bits each)
|
||||
| #RRGGBB (8 bits each)
|
||||
| #RRRGGGBBB (12 bits each)
|
||||
| #RRRRGGGGBBBB (16 bits each)
|
||||
| The R, G, and B represent single hexadecimal digits. When fewer than 16 bits
|
||||
each are specified, they represent the most significant bits of the value
|
||||
(unlike the “rgb:” syntax, in which values are scaled). For example,
|
||||
the string ``#3a7`` is the same as ``#3000a0007000``.
|
||||
|
||||
rgbi:<red>/<green>/<blue>
|
||||
``rgbi:<red>/<green>/<blue>``
|
||||
red, green, and blue are floating-point values between 0.0 and 1.0, inclusive. The input format for these values is an optional
|
||||
sign, a string of numbers possibly containing a decimal point, and an optional exponent field containing an E or e followed by a possibly
|
||||
signed integer string.
|
||||
|
||||
10
docs/faq.rst
10
docs/faq.rst
@@ -280,7 +280,7 @@ with::
|
||||
On macOS, you can open *Font Book* and look in the :guilabel:`Fixed width`
|
||||
collection to see all monospaced fonts on your system.
|
||||
|
||||
Note that the spacing property is calculated by fontconfig based on actual glyph
|
||||
Note that **on Linux**, the spacing property is calculated by fontconfig based on actual glyph
|
||||
widths in the font. If for some reason fontconfig concludes your favorite
|
||||
monospace font does not have ``spacing=100`` you can override it by using the
|
||||
following :file:`~/.config/fontconfig/fonts.conf`::
|
||||
@@ -346,6 +346,10 @@ many alternate icons available, click on an icon to visit its homepage:
|
||||
:target: https://github.com/samholmes/whiskers
|
||||
:width: 256
|
||||
|
||||
.. image:: https://github.com/user-attachments/assets/a37d7830-4a8c-45a8-988a-3e98a41ea541
|
||||
:target: https://github.com/diegobit/kitty-icon
|
||||
:width: 256
|
||||
|
||||
.. image:: https://github.com/eccentric-j/eccentric-icons/raw/main/icons/kitty-terminal/2d/kitty-preview.png
|
||||
:target: https://github.com/eccentric-j/eccentric-icons
|
||||
:width: 256
|
||||
@@ -399,9 +403,11 @@ This is accomplished by using ``map`` with :ac:`send_key` in :file:`kitty.conf`.
|
||||
For example::
|
||||
|
||||
map alt+s send_key ctrl+s
|
||||
map ctrl+alt+2 combine : send_key ctrl+c : send_key h : send_key a
|
||||
|
||||
This causes the program running in kitty to receive the :kbd:`ctrl+s` key when
|
||||
you press the :kbd:`alt+s` key. To see this in action, run::
|
||||
you press the :kbd:`alt+s` key and several keystrokes when you press
|
||||
:kbd:`ctrl+alt+2`. To see this in action, run::
|
||||
|
||||
kitten show-key -m kitty
|
||||
|
||||
|
||||
@@ -98,6 +98,9 @@ get_release_url() {
|
||||
get_file_url "v$release_version" "$release_version"
|
||||
}
|
||||
|
||||
get_version_url() {
|
||||
get_file_url "v$1" "$1"
|
||||
}
|
||||
|
||||
get_nightly_url() {
|
||||
get_file_url "nightly" "nightly"
|
||||
@@ -108,6 +111,7 @@ get_download_url() {
|
||||
case "$installer" in
|
||||
"nightly") get_nightly_url ;;
|
||||
"") get_release_url ;;
|
||||
version-*) get_version_url "${installer#*-}";;
|
||||
*) installer_is_file="y" ;;
|
||||
esac
|
||||
}
|
||||
@@ -126,20 +130,24 @@ download_installer() {
|
||||
}
|
||||
}
|
||||
|
||||
ensure_dest() {
|
||||
printf "%s\n" "Installing to $dest"
|
||||
command rm -rf "$dest" || die "Failed to delete $dest"
|
||||
command mkdir -p "$dest" || die "Failed to mkdir -p $dest"
|
||||
command rm -rf "$dest" || die "Failed to delete $dest"
|
||||
}
|
||||
|
||||
linux_install() {
|
||||
command mkdir "$tdir/mp"
|
||||
command tar -C "$tdir/mp" "-xJof" "$installer" || die "Failed to extract kitty tarball"
|
||||
printf "%s\n" "Installing to $dest"
|
||||
command rm -rf "$dest" || die "Failed to delete $dest"
|
||||
ensure_dest
|
||||
command mv "$tdir/mp" "$dest" || die "Failed to move kitty.app to $dest"
|
||||
}
|
||||
|
||||
macos_install() {
|
||||
command mkdir "$tdir/mp"
|
||||
command hdiutil attach "$installer" "-mountpoint" "$tdir/mp" || die "Failed to mount kitty.dmg"
|
||||
printf "%s\n" "Installing to $dest"
|
||||
command rm -rf "$dest"
|
||||
command mkdir -p "$dest" || die "Failed to create the directory: $dest"
|
||||
ensure_dest
|
||||
command ditto -v "$tdir/mp/kitty.app" "$dest"
|
||||
rc="$?"
|
||||
command hdiutil detach "$tdir/mp"
|
||||
|
||||
@@ -38,11 +38,15 @@ In addition to kitty, this protocol is also implemented in:
|
||||
* The `WezTerm terminal <https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html>`__
|
||||
* The `alacritty terminal <https://github.com/alacritty/alacritty/pull/7125>`__
|
||||
* The `rio terminal <https://github.com/raphamorim/rio/commit/cd463ca37677a0fc48daa8795ea46dadc92b1e95>`__
|
||||
* The `notcurses library
|
||||
<https://github.com/dankamongmen/notcurses/issues/2131>`__
|
||||
* The `crossterm library
|
||||
<https://github.com/crossterm-rs/crossterm/pull/688>`__
|
||||
|
||||
Libraries implementing this protocol:
|
||||
|
||||
* The `notcurses library <https://github.com/dankamongmen/notcurses/issues/2131>`__
|
||||
* The `crossterm library <https://github.com/crossterm-rs/crossterm/pull/688>`__
|
||||
* The `textual library <https://github.com/Textualize/textual/pull/4631>`__
|
||||
|
||||
Programs implementing this protocol:
|
||||
|
||||
* The `Vim text editor <https://github.com/vim/vim/commit/63a2e360cca2c70ab0a85d14771d3259d4b3aafa>`__
|
||||
* The `Emacs text editor via the kkp package <https://github.com/benjaminor/kkp>`__
|
||||
* The `Neovim text editor <https://github.com/neovim/neovim/pull/18181>`__
|
||||
@@ -52,6 +56,10 @@ In addition to kitty, this protocol is also implemented in:
|
||||
* The `far2l file manager <https://github.com/elfmz/far2l/commit/e1f2ee0ef2b8332e5fa3ad7f2e4afefe7c96fc3b>`__
|
||||
* The `Yazi file manager <https://github.com/sxyazi/yazi>`__
|
||||
* The `awrit web browser <https://github.com/chase/awrit>`__
|
||||
* The `Turbo Vision <https://github.com/magiblot/tvision/commit/6e5a7b46c6634079feb2ac98f0b890bbed59f1ba>`/`Free Vision <https://gitlab.com/freepascal.org/fpc/source/-/issues/40673#note_2061428120>`__ IDEs
|
||||
|
||||
Shells implementing this protocol:
|
||||
|
||||
* The `nushell shell <https://github.com/nushell/nushell/pull/10540>`__
|
||||
* The `fish shell <https://github.com/fish-shell/fish-shell/commit/8bf8b10f685d964101f491b9cc3da04117a308b4>`__
|
||||
|
||||
|
||||
@@ -73,6 +73,9 @@ would pass to ``kitten @``. For example:
|
||||
shown above or ``--self``.
|
||||
|
||||
|
||||
Run, ``kitten @ --help`` in a kitty terminal, to see all the remote control
|
||||
commands available to you.
|
||||
|
||||
Passing arguments to kittens
|
||||
------------------------------
|
||||
|
||||
|
||||
@@ -114,11 +114,14 @@ Watching launched windows
|
||||
|
||||
The :option:`launch --watcher` option allows you to specify Python functions
|
||||
that will be called at specific events, such as when the window is resized or
|
||||
closed. Simply specify the path to a Python module that specifies callback
|
||||
functions for the events you are interested in, for example:
|
||||
closed. Note that you can also specify watchers that are loaded for all windows,
|
||||
via :opt:`watcher`. To create a watcher, specify the path to a Python module
|
||||
that specifies callback functions for the events you are interested in, for
|
||||
create :file:`~/.config/kitty/mywatcher.py` and use :option:`launch --watcher` = :file:`mywatcher.py`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# ~/.config/kitty/mywatcher.py
|
||||
from typing import Any, Dict
|
||||
|
||||
from kitty.boss import Boss
|
||||
@@ -127,36 +130,53 @@ functions for the events you are interested in, for example:
|
||||
|
||||
def on_resize(boss: Boss, window: Window, data: Dict[str, Any]) -> None:
|
||||
# Here data will contain old_geometry and new_geometry
|
||||
# Note that resize is also called the first time a window is created
|
||||
# which can be detected as old_geometry will have all zero values, in
|
||||
# particular, old_geometry.xnum and old_geometry.ynum will be zero.
|
||||
...
|
||||
|
||||
def on_focus_change(boss: Boss, window: Window, data: Dict[str, Any])-> None:
|
||||
# Here data will contain focused
|
||||
...
|
||||
|
||||
def on_close(boss: Boss, window: Window, data: Dict[str, Any])-> None:
|
||||
# called when window is closed, typically when the program running in
|
||||
# it exits
|
||||
...
|
||||
|
||||
def on_set_user_var(boss: Boss, window: Window, data: Dict[str, Any]) -> None:
|
||||
# called when a "user variable" is set or deleted on a window. Here
|
||||
# data will contain key and value
|
||||
...
|
||||
|
||||
def on_title_change(boss: Boss, window: Window, data: Dict[str, Any]) -> None:
|
||||
# called when the window title is changed on a window. Here
|
||||
# data will contain title and from_child. from_child will be True
|
||||
# when a title change was requested via escape code from the program
|
||||
# running in the terminal
|
||||
...
|
||||
|
||||
def on_cmd_startstop(boss: Boss, window: Window, data: Dict[str, Any]) -> None:
|
||||
# called when the shell starts/stops executing a command. Here
|
||||
# data will contain is_start, cmdline and time.
|
||||
...
|
||||
|
||||
Every callback is passed a reference to the global ``Boss`` object as well as
|
||||
the ``Window`` object the action is occurring on. The ``data`` object is a dict
|
||||
that contains event dependent data. Some useful methods and attributes for the
|
||||
``Window`` object are: ``as_text(as_ans=False, add_history=False,
|
||||
add_wrap_markers=False, alternate_screen=False)`` with which you can get the
|
||||
contents of the window and its scrollback buffer. Similarly,
|
||||
``window.child.pid`` is the PID of the processes that was launched
|
||||
in the window and ``window.id`` is the internal kitty ``id`` of the window.
|
||||
that contains event dependent data. You have full access to kitty internals in
|
||||
the watcher scripts, however kitty internals are not documented/stable so for
|
||||
most things you are better off using the kitty :doc:`Remote control API </remote-control>`.
|
||||
Simply call :code:`boss.call_remote_control()`, with the same arguments you
|
||||
would pass to ``kitten @``. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def on_resize(boss: Boss, window: Window, data: Dict[str, Any]) -> None:
|
||||
# send some text to the resized window
|
||||
boss.call_remote_control(window, ('send-text', f'--match=id:{window.id}', 'hello world'))
|
||||
|
||||
Run, ``kitten @ --help`` in a kitty terminal, to see all the remote control
|
||||
commands available to you.
|
||||
|
||||
|
||||
Finding executables
|
||||
|
||||
@@ -110,7 +110,7 @@ func run_plain_text_loop(opts *Options) (err error) {
|
||||
defer tempfile.Close()
|
||||
}
|
||||
}
|
||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
|
||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ func parse_aliases(raw []string) (map[string][]string, error) {
|
||||
}
|
||||
|
||||
func run_get_loop(opts *Options, args []string) (err error) {
|
||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
|
||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ func (self *Input) has_mime_matching(predicate func(string) bool) bool {
|
||||
}
|
||||
|
||||
func write_loop(inputs []*Input, opts *Options) (err error) {
|
||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
|
||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func DetectSupport(timeout time.Duration) (memory, files, direct bool, err error
|
||||
temp_files_to_delete := make([]string, 0, 8)
|
||||
shm_files_to_delete := make([]shm.MMap, 0, 8)
|
||||
var direct_query_id, file_query_id, memory_query_id uint32
|
||||
lp, e := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
|
||||
lp, e := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
|
||||
if e != nil {
|
||||
err = e
|
||||
return
|
||||
|
||||
@@ -112,7 +112,7 @@ func (p *parsed_data) generate_chunks(callback func(string)) {
|
||||
}
|
||||
|
||||
func (p *parsed_data) run_loop() (err error) {
|
||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking)
|
||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking, loop.NoInBandResizeNotifications)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ with the specified identifier is closed.
|
||||
|
||||
usage = 'TITLE [BODY ...]'
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit('This should be run as kitten clipboard')
|
||||
raise SystemExit('This should be run as `kitten notify ...`')
|
||||
elif __name__ == '__doc__':
|
||||
cd = sys.cli_docs # type: ignore
|
||||
cd['usage'] = usage
|
||||
|
||||
@@ -26,7 +26,7 @@ func main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {
|
||||
queries[i] = x
|
||||
}
|
||||
}
|
||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoKeyboardStateChange, loop.NoMouseTracking, loop.NoRestoreColors)
|
||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoKeyboardStateChange, loop.NoMouseTracking, loop.NoRestoreColors, loop.NoInBandResizeNotifications)
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ void main() {
|
||||
int mark = int(text_attrs >> MARK_SHIFT) & MARK_MASK;
|
||||
uint has_mark = uint(step(1, float(mark)));
|
||||
uint bg_as_uint = resolve_color(colors[bg_index], default_colors[bg_index]);
|
||||
bg_as_uint = has_mark * color_table[NUM_COLORS + mark] + (ONE - has_mark) * bg_as_uint;
|
||||
bg_as_uint = has_mark * color_table[NUM_COLORS + mark - 1] + (ONE - has_mark) * bg_as_uint;
|
||||
vec3 bg = color_to_vec(bg_as_uint);
|
||||
uint fg_as_uint = resolve_color(colors[fg_index], default_colors[fg_index]);
|
||||
// }}}
|
||||
@@ -169,7 +169,7 @@ void main() {
|
||||
|
||||
|
||||
// Foreground
|
||||
fg_as_uint = has_mark * color_table[NUM_COLORS + MARK_MASK + 1 + mark] + (ONE - has_mark) * fg_as_uint;
|
||||
fg_as_uint = has_mark * color_table[NUM_COLORS + MARK_MASK + mark] + (ONE - has_mark) * fg_as_uint;
|
||||
foreground = color_to_vec(fg_as_uint);
|
||||
float has_dim = float((text_attrs >> DIM_SHIFT) & ONE);
|
||||
effective_text_alpha = inactive_text_alpha * mix(1.0, dim_opacity, has_dim);
|
||||
|
||||
@@ -289,7 +289,12 @@ class WriteRequest:
|
||||
|
||||
def write_base64_data(self, b: bytes) -> None:
|
||||
if not self.max_size_exceeded:
|
||||
decoded = self.decoder.decode(b)
|
||||
try:
|
||||
decoded = self.decoder.decode(b)
|
||||
except ValueError:
|
||||
log_error('Clipboard write request has invalid data, ignoring this chunk of data')
|
||||
self.decoder.reset()
|
||||
decoded = b''
|
||||
if decoded:
|
||||
self.tempfile.write(decoded)
|
||||
if self.max_size > 0 and self.tempfile.tell() > (self.max_size * 1024 * 1024):
|
||||
|
||||
@@ -23,7 +23,7 @@ class Version(NamedTuple):
|
||||
|
||||
appname: str = 'kitty'
|
||||
kitty_face = '🐱'
|
||||
version: Version = Version(0, 36, 0)
|
||||
version: Version = Version(0, 36, 1)
|
||||
str_version: str = '.'.join(map(str, version))
|
||||
_plat = sys.platform.lower()
|
||||
is_macos: bool = 'darwin' in _plat
|
||||
|
||||
@@ -854,10 +854,11 @@ render_sample_text(CTFace *self, PyObject *args) {
|
||||
}
|
||||
render_glyphs(font, canvas_width, canvas_height, baseline, num_glyphs);
|
||||
uint8_t r = (fg >> 16) & 0xff, g = (fg >> 8) & 0xff, b = fg & 0xff;
|
||||
const uint8_t *last_pixel = (uint8_t*)PyBytes_AS_STRING(pbuf) + PyBytes_GET_SIZE(pbuf) - sizeof(pixel);
|
||||
for (
|
||||
uint8_t *p = (uint8_t*)PyBytes_AS_STRING(pbuf), *s = buffers.render_buf;
|
||||
p < (uint8_t*)PyBytes_AS_STRING(pbuf) + sizeof(pixel) * canvas_width * canvas_height;
|
||||
p += 4, s++
|
||||
p <= last_pixel;
|
||||
p += sizeof(pixel), s++
|
||||
) {
|
||||
p[0] = r; p[1] = g; p[2] = b; p[3] = s[0];
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ static int __eq__(Cursor *a, Cursor *b) {
|
||||
return EQ(bold) && EQ(italic) && EQ(strikethrough) && EQ(dim) && EQ(reverse) && EQ(decoration) && EQ(fg) && EQ(bg) && EQ(decoration_fg) && EQ(x) && EQ(y) && EQ(shape) && EQ(non_blinking);
|
||||
}
|
||||
|
||||
static const char* cursor_names[NUM_OF_CURSOR_SHAPES] = { "NO_SHAPE", "BLOCK", "BEAM", "UNDERLINE" };
|
||||
static const char* cursor_names[NUM_OF_CURSOR_SHAPES] = { "NO_SHAPE", "BLOCK", "BEAM", "UNDERLINE", "HOLLOW" };
|
||||
|
||||
#define BOOL(x) ((x) ? Py_True : Py_False)
|
||||
static PyObject *
|
||||
|
||||
@@ -728,6 +728,7 @@ PyInit_fast_data_types(void) {
|
||||
PyModule_AddIntMacro(m, CURSOR_BLOCK);
|
||||
PyModule_AddIntMacro(m, CURSOR_BEAM);
|
||||
PyModule_AddIntMacro(m, CURSOR_UNDERLINE);
|
||||
PyModule_AddIntMacro(m, CURSOR_HOLLOW);
|
||||
PyModule_AddIntMacro(m, NO_CURSOR_SHAPE);
|
||||
PyModule_AddIntMacro(m, DECAWM);
|
||||
PyModule_AddIntMacro(m, DECCOLM);
|
||||
|
||||
@@ -68,7 +68,7 @@ typedef uint16_t glyph_index;
|
||||
typedef uint32_t pixel;
|
||||
typedef unsigned int index_type;
|
||||
typedef uint16_t sprite_index;
|
||||
typedef enum CursorShapes { NO_CURSOR_SHAPE, CURSOR_BLOCK, CURSOR_BEAM, CURSOR_UNDERLINE, NUM_OF_CURSOR_SHAPES } CursorShape;
|
||||
typedef enum CursorShapes { NO_CURSOR_SHAPE, CURSOR_BLOCK, CURSOR_BEAM, CURSOR_UNDERLINE, CURSOR_HOLLOW, NUM_OF_CURSOR_SHAPES } CursorShape;
|
||||
typedef enum { DISABLE_LIGATURES_NEVER, DISABLE_LIGATURES_CURSOR, DISABLE_LIGATURES_ALWAYS } DisableLigature;
|
||||
|
||||
#define ERROR_PREFIX "[PARSE ERROR]"
|
||||
|
||||
@@ -256,6 +256,7 @@ GLFW_RELEASE: int
|
||||
GLFW_REPEAT: int
|
||||
CURSOR_BEAM: int
|
||||
CURSOR_BLOCK: int
|
||||
CURSOR_HOLLOW: int
|
||||
NO_CURSOR_SHAPE: int
|
||||
CURSOR_UNDERLINE: int
|
||||
DECAWM: int
|
||||
|
||||
@@ -325,8 +325,8 @@ read_fvar_font_table(const uint8_t *table, size_t table_len, PyObject *name_look
|
||||
const double minimum = next32, def = next32, maximum = next32;
|
||||
p = (uint16_t*)(pos + 16);
|
||||
int32_t flags = next, strid = next;
|
||||
PyObject *axis = Py_BuildValue("{sd sd sd ss# sO sN}",
|
||||
"minimum", minimum, "maximum", maximum, "default", def, "tag", pos, 4,
|
||||
PyObject *axis = Py_BuildValue("{sd sd sd sN sO sN}",
|
||||
"minimum", minimum, "maximum", maximum, "default", def, "tag", PyUnicode_FromStringAndSize((const char*)pos, 4),
|
||||
"hidden", (flags & 1) ? Py_True : Py_False, "strid", get_best_name(name_lookup_table, strid)
|
||||
); if (!axis) return NULL;
|
||||
PyTuple_SET_ITEM(axes, i, axis);
|
||||
|
||||
@@ -195,7 +195,7 @@ def find_best_match(
|
||||
# Let CoreText choose the font if the family exists, otherwise
|
||||
# fallback to Menlo
|
||||
if q not in font_map['family_map']:
|
||||
if family != "monospace":
|
||||
if family.lower() not in ('monospace', 'symbols nerd font mono'):
|
||||
log_error(f'The font {family} was not found, falling back to Menlo')
|
||||
q = 'menlo'
|
||||
candidates = scorer.sorted_candidates(font_map['family_map'][q])
|
||||
|
||||
@@ -1013,7 +1013,8 @@ render_sample_text(Face *self, PyObject *args) {
|
||||
place_bitmap_in_canvas(canvas, &pbm, canvas_width, canvas_height, x, 0, baseline, 99999, fg, 0, y);
|
||||
}
|
||||
|
||||
for (uint8_t *p = (uint8_t*)PyBytes_AS_STRING(pbuf); p < (uint8_t*)PyBytes_AS_STRING(pbuf) + sizeof(pixel) * canvas_width * canvas_height; p += 4) {
|
||||
const uint8_t *last_pixel = (uint8_t*)PyBytes_AS_STRING(pbuf) + PyBytes_GET_SIZE(pbuf) - sizeof(pixel);
|
||||
for (uint8_t *p = (uint8_t*)PyBytes_AS_STRING(pbuf); p <= last_pixel; p += sizeof(pixel)) {
|
||||
uint8_t a = p[0], b = p[1], g = p[2], r = p[3];
|
||||
p[0] = r; p[1] = g; p[2] = b; p[3] = a;
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ bool
|
||||
init_logging(PyObject *module) {
|
||||
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||
#ifdef __APPLE__
|
||||
// This env var can be either 1 or 2
|
||||
if (getenv("KITTY_LAUNCHED_BY_LAUNCH_SERVICES") != NULL) use_os_log = true;
|
||||
#endif
|
||||
return true;
|
||||
|
||||
@@ -320,6 +320,11 @@ def macos_cmdline(argv_args: list[str]) -> list[str]:
|
||||
ans = list(shlex_split(raw))
|
||||
if ans and ans[0] == 'kitty':
|
||||
del ans[0]
|
||||
if '-1' in ans or '--single-instance' in ans:
|
||||
# Re-exec with new argv so that the C code that handles single instance
|
||||
# can pick up the modified argv
|
||||
os.environ['KITTY_LAUNCHED_BY_LAUNCH_SERVICES'] = '2' # so that use_os_log is set in the re-execed process
|
||||
os.execl(kitty_exe(), 'kitty', *(ans + argv_args))
|
||||
return ans + argv_args
|
||||
|
||||
|
||||
@@ -446,7 +451,6 @@ def _main() -> None:
|
||||
if is_macos and os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES', None) == '1':
|
||||
os.chdir(os.path.expanduser('~'))
|
||||
args = macos_cmdline(args)
|
||||
getattr(sys, 'kitty_run_data')['launched_by_launch_services'] = True
|
||||
try:
|
||||
cwd_ok = os.path.isdir(os.getcwd())
|
||||
except Exception:
|
||||
|
||||
@@ -189,7 +189,12 @@ class EncodedDataStore:
|
||||
def add_base64_data(self, data: Union[str, bytes]) -> None:
|
||||
if isinstance(data, str):
|
||||
data = data.encode('ascii')
|
||||
self.data_store(self.decoder.decode(data))
|
||||
try:
|
||||
decoded = self.decoder.decode(data)
|
||||
except ValueError:
|
||||
log_error('Ignoring invalid base64 encoded data in notification request')
|
||||
else:
|
||||
self.data_store(decoded)
|
||||
|
||||
def flush_encoded_data(self) -> None:
|
||||
self.decoder.reset()
|
||||
|
||||
@@ -315,7 +315,7 @@ cursor shape to :code:`beam` at shell prompts. You can avoid this by setting
|
||||
opt('cursor_shape_unfocused', 'hollow', option_type='to_cursor_unfocused_shape', ctype='int', long_text='''
|
||||
Defines the text cursor shape when the OS window is not focused. The unfocused
|
||||
cursor shape can be one of :code:`block`, :code:`beam`, :code:`underline`,
|
||||
:code:`hollow`.
|
||||
:code:`hollow` and :code:`unchanged` (leave the cursor shape as it is).
|
||||
''')
|
||||
|
||||
opt('cursor_beam_thickness', '1.5',
|
||||
@@ -1529,7 +1529,7 @@ opt('background_image_linear', 'no',
|
||||
|
||||
opt('second_transparent_bg', 'none', option_type='to_color_or_none', long_text='''
|
||||
When the background color matches this color, :opt:`background_opacity` is applied to it
|
||||
to render it as semi-transparent, just as for colors matching the background color.
|
||||
to render it as semi-transparent, just as for colors matching the main :opt:`background` color.
|
||||
Useful in more complex UIs like editors where you could want more than a single background color
|
||||
to be rendered as transparent, for instance, for a cursor highlight line background.
|
||||
Terminal applications can set this color using :ref:`The kitty color control <color_control>`
|
||||
|
||||
2
kitty/options/types.py
generated
2
kitty/options/types.py
generated
@@ -507,7 +507,7 @@ class Options:
|
||||
cursor_beam_thickness: float = 1.5
|
||||
cursor_blink_interval: typing.Tuple[float, kitty.options.utils.EasingFunction, kitty.options.utils.EasingFunction] = (-1.0, kitty.options.utils.EasingFunction(), kitty.options.utils.EasingFunction())
|
||||
cursor_shape: int = 1
|
||||
cursor_shape_unfocused: int = 0
|
||||
cursor_shape_unfocused: int = 4
|
||||
cursor_stop_blinking_after: float = 15.0
|
||||
cursor_text_color: typing.Optional[kitty.fast_data_types.Color] = Color(17, 17, 17)
|
||||
cursor_underline_thickness: float = 2.0
|
||||
|
||||
@@ -46,7 +46,7 @@ from kitty.conf.utils import (
|
||||
unit_float,
|
||||
)
|
||||
from kitty.constants import is_macos
|
||||
from kitty.fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, NO_CURSOR_SHAPE, Color, Shlex, SingleKey
|
||||
from kitty.fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_HOLLOW, CURSOR_UNDERLINE, NO_CURSOR_SHAPE, Color, Shlex, SingleKey
|
||||
from kitty.fonts import FontModification, FontSpec, ModificationType, ModificationUnit, ModificationValue
|
||||
from kitty.key_names import character_key_name_aliases, functional_key_name_aliases, get_key_name_lookup
|
||||
from kitty.rgb import color_as_int
|
||||
@@ -530,7 +530,8 @@ cshapes_unfocused = {
|
||||
'block': CURSOR_BLOCK,
|
||||
'beam': CURSOR_BEAM,
|
||||
'underline': CURSOR_UNDERLINE,
|
||||
'hollow': NO_CURSOR_SHAPE
|
||||
'hollow': CURSOR_HOLLOW,
|
||||
'unchanged': NO_CURSOR_SHAPE,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2442,8 +2442,7 @@ screen_request_capabilities(Screen *self, char c, const char *query) {
|
||||
if (strcmp(" q", query) == 0) {
|
||||
// cursor shape DECSCUSR
|
||||
switch(self->cursor->shape) {
|
||||
case NO_CURSOR_SHAPE:
|
||||
case NUM_OF_CURSOR_SHAPES:
|
||||
case NO_CURSOR_SHAPE: case CURSOR_HOLLOW: case NUM_OF_CURSOR_SHAPES:
|
||||
shape = 1; break;
|
||||
case CURSOR_BLOCK:
|
||||
shape = self->cursor->non_blinking ? 2 : 0; break;
|
||||
|
||||
@@ -327,27 +327,17 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c
|
||||
if (cursor->opacity > 0) {
|
||||
rd->cursor_x = cursor->x, rd->cursor_y = cursor->y;
|
||||
rd->cursor_opacity = cursor->opacity;
|
||||
if (cursor->is_focused) {
|
||||
switch(cursor->shape) {
|
||||
default:
|
||||
rd->cursor_fg_sprite_idx = BLOCK_IDX; break;
|
||||
case CURSOR_BEAM:
|
||||
rd->cursor_fg_sprite_idx = BEAM_IDX; break;
|
||||
case CURSOR_UNDERLINE:
|
||||
rd->cursor_fg_sprite_idx = UNDERLINE_IDX; break;
|
||||
}
|
||||
} else {
|
||||
switch(OPT(cursor_shape_unfocused)) {
|
||||
default:
|
||||
rd->cursor_fg_sprite_idx = UNFOCUSED_IDX; break;
|
||||
case CURSOR_BEAM:
|
||||
rd->cursor_fg_sprite_idx = BEAM_IDX; break;
|
||||
case CURSOR_UNDERLINE:
|
||||
rd->cursor_fg_sprite_idx = UNDERLINE_IDX; break;
|
||||
case CURSOR_BLOCK:
|
||||
rd->cursor_fg_sprite_idx = BLOCK_IDX; break;
|
||||
}
|
||||
}
|
||||
CursorShape cs = (cursor->is_focused || OPT(cursor_shape_unfocused) == NO_CURSOR_SHAPE) ? cursor->shape : OPT(cursor_shape_unfocused);
|
||||
switch(cs) {
|
||||
case CURSOR_BEAM:
|
||||
rd->cursor_fg_sprite_idx = BEAM_IDX; break;
|
||||
case CURSOR_UNDERLINE:
|
||||
rd->cursor_fg_sprite_idx = UNDERLINE_IDX; break;
|
||||
case CURSOR_BLOCK: case NUM_OF_CURSOR_SHAPES: case NO_CURSOR_SHAPE:
|
||||
rd->cursor_fg_sprite_idx = BLOCK_IDX; break;
|
||||
case CURSOR_HOLLOW:
|
||||
rd->cursor_fg_sprite_idx = UNFOCUSED_IDX; break;
|
||||
};
|
||||
color_type cell_fg = rd->default_fg, cell_bg = rd->default_bg;
|
||||
index_type cell_color_x = cursor->x;
|
||||
bool reversed = false;
|
||||
|
||||
@@ -92,11 +92,10 @@ write_control_ch(Shlex *self) {
|
||||
|
||||
static void
|
||||
read_valid_digits(Shlex *self, int max, char *output, bool(*is_valid)(Py_UCS4 ch)) {
|
||||
for (int i = 0; i < max && self->src_pos < self->src_sz; i++) {
|
||||
Py_UCS4 ch = PyUnicode_READ(self->kind, self->src_data, self->src_pos);
|
||||
if (!is_valid(ch)) break;
|
||||
output[0] = ch;
|
||||
self->src_pos++; output++;
|
||||
for (int i = 0; i < max && self->src_pos < self->src_sz; i++, output++) {
|
||||
Py_UCS4 ch = read_ch(self);
|
||||
if (!is_valid(ch)) { self->src_pos--; break; }
|
||||
*output = ch;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,6 +124,8 @@ def find_testable_go_packages() -> tuple[set[str], dict[str, list[str]]]:
|
||||
base = os.getcwd()
|
||||
pat = re.compile(r'^func Test([A-Z]\w+)', re.MULTILINE)
|
||||
for (dirpath, dirnames, filenames) in os.walk(base):
|
||||
if 'b' in dirnames and os.path.basename(dirpath) == 'bypy':
|
||||
dirnames.remove('b')
|
||||
for f in filenames:
|
||||
if f.endswith('_test.go'):
|
||||
q = os.path.relpath(dirpath, base)
|
||||
|
||||
@@ -27,7 +27,11 @@ func is_stream_response(serialized_response []byte) bool {
|
||||
|
||||
func do_chunked_io(io_data *rc_io_data) (serialized_response []byte, err error) {
|
||||
serialized_response = make([]byte, 0)
|
||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors)
|
||||
// we cant do inbandresize notification as in the --no-response case the
|
||||
// command can cause a resize and the loop can quit before the notification
|
||||
// arrives, leading to the notification being sent to whatever is executed
|
||||
// after us.
|
||||
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoInBandResizeNotifications)
|
||||
if io_data.on_key_event != nil {
|
||||
lp.FullKeyboardProtocol()
|
||||
} else {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -362,11 +363,22 @@ func ReloadConfigInKitty(in_parent_only bool) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if all, err := process.Processes(); err == nil {
|
||||
for _, p := range all {
|
||||
if exe, eerr := p.Exe(); eerr == nil {
|
||||
if c, err := p.CmdlineSlice(); err == nil && is_kitty_gui_cmdline(exe, c...) {
|
||||
_ = p.SendSignal(unix.SIGUSR1)
|
||||
// process.Processes() followed by filtering by getting the process
|
||||
// exe and cmdline is very slow on non-Linux systems as CGO is not allowed
|
||||
// which means getting exe works by calling lsof on every process. So instead do
|
||||
// initial filtering based on ps output.
|
||||
if ps_out, err := exec.Command("ps", "-x", "-o", "pid=,comm=").Output(); err == nil {
|
||||
for _, line := range utils.Splitlines(utils.UnsafeBytesToString(ps_out)) {
|
||||
line = strings.TrimSpace(line)
|
||||
if pid_string, argv0, found := strings.Cut(line, " "); found {
|
||||
if pid, err := strconv.Atoi(strings.TrimSpace(pid_string)); err == nil && strings.Contains(argv0, "kitty") {
|
||||
if p, err := process.NewProcess(int32(pid)); err == nil {
|
||||
if cmdline, err := p.CmdlineSlice(); err == nil {
|
||||
if exe, err := p.Exe(); err == nil && is_kitty_gui_cmdline(exe, cmdline...) {
|
||||
_ = p.SendSignal(unix.SIGUSR1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +196,10 @@ func NoRestoreColors(self *Loop) {
|
||||
self.terminal_options.restore_colors = false
|
||||
}
|
||||
|
||||
func NoInBandResizeNotifications(self *Loop) {
|
||||
self.terminal_options.in_band_resize_notification = false
|
||||
}
|
||||
|
||||
func (self *Loop) DeathSignalName() string {
|
||||
if self.death_signal != SIGNULL {
|
||||
return self.death_signal.String()
|
||||
|
||||
@@ -26,6 +26,7 @@ func new_loop() *Loop {
|
||||
l := Loop{controlling_term: nil}
|
||||
l.terminal_options.Alternate_screen = true
|
||||
l.terminal_options.restore_colors = true
|
||||
l.terminal_options.in_band_resize_notification = true
|
||||
l.terminal_options.kitty_keyboard_mode = DISAMBIGUATE_KEYS | REPORT_ALTERNATE_KEYS | REPORT_ALL_KEYS_AS_ESCAPE_CODES | REPORT_TEXT_WITH_KEYS
|
||||
l.escape_code_parser.HandleCSI = l.handle_csi
|
||||
l.escape_code_parser.HandleOSC = l.handle_osc
|
||||
|
||||
@@ -100,6 +100,7 @@ type TerminalStateOptions struct {
|
||||
Alternate_screen, restore_colors bool
|
||||
mouse_tracking MouseTracking
|
||||
kitty_keyboard_mode KeyboardStateBits
|
||||
in_band_resize_notification bool
|
||||
}
|
||||
|
||||
func set_modes(sb *strings.Builder, modes ...Mode) {
|
||||
@@ -128,7 +129,10 @@ func (self *TerminalStateOptions) SetStateEscapeCodes() string {
|
||||
reset_modes(&sb,
|
||||
IRM, DECKM, DECSCNM, BRACKETED_PASTE, FOCUS_TRACKING,
|
||||
MOUSE_BUTTON_TRACKING, MOUSE_MOTION_TRACKING, MOUSE_MOVE_TRACKING, MOUSE_UTF8_MODE, MOUSE_SGR_MODE)
|
||||
set_modes(&sb, DECARM, DECAWM, DECTCEM, INBAND_RESIZE_NOTIFICATION)
|
||||
set_modes(&sb, DECARM, DECAWM, DECTCEM)
|
||||
if self.in_band_resize_notification {
|
||||
set_modes(&sb, INBAND_RESIZE_NOTIFICATION)
|
||||
}
|
||||
if self.Alternate_screen {
|
||||
set_modes(&sb, ALTERNATE_SCREEN)
|
||||
sb.WriteString(CLEAR_SCREEN)
|
||||
|
||||
@@ -49,13 +49,15 @@ with open(__file__, 'rb') as f:
|
||||
script = f.read().decode('utf-8')
|
||||
script = script[:script.find('# EOF_REMOTE')].replace('if False:', 'if True:', 1)
|
||||
with tempfile.NamedTemporaryFile(prefix='install-dmg-', suffix='.py') as f:
|
||||
cmd = 'python ../bypy macos program --dont-strip'
|
||||
cmd = 'python ../bypy macos program'
|
||||
if 'dont_sign' not in sys.argv:
|
||||
cmd += ' --sign-installers'
|
||||
if 'notarize' in sys.argv:
|
||||
cmd += ' --sign-installers --notarize'
|
||||
if 'strip' not in sys.argv:
|
||||
cmd += ' --dont-strip'
|
||||
if 'tests' not in sys.argv:
|
||||
cmd += ' --skip-tests'
|
||||
if 'notarize' in sys.argv:
|
||||
cmd += ' --sign-installers --notarize'
|
||||
run(cmd)
|
||||
f.write(script.encode('utf-8'))
|
||||
f.flush()
|
||||
|
||||
Reference in New Issue
Block a user