Compare commits

..

43 Commits

Author SHA1 Message Date
Kovid Goyal
4b0724530e Fix #2912 2020-08-11 14:19:32 +05:30
Kovid Goyal
e5681a93f9 version 0.18.3 2020-08-11 13:38:38 +05:30
Kovid Goyal
fb838a694e ... 2020-08-11 13:08:16 +05:30
Kovid Goyal
8c6f188767 Forgot to use " for key names 2020-08-11 13:01:33 +05:30
Kovid Goyal
55e15dc79e Properly type check os window initial size function
Also use the sizes specified int he session file in preference to those
specified on the command line. Fixes #2909
2020-08-11 09:28:53 +05:30
Kovid Goyal
514073cb75 Fix #2911 2020-08-11 07:52:32 +05:30
Kovid Goyal
f6ece6522e Fix a regression that broke specifying OS window size in the session file
Fixes #2908
2020-08-10 21:55:54 +05:30
Kovid Goyal
817f0997cf Fix one pixel mis-alignment of rounded corners when either the cell dimensions or the thickness of the line is an odd number of pixels
The control points of the Bezier curve and the spread due to thickness
must match up with the values used to draw straight lines. In order to
ensure that, one calculates them before upscaling by the super sampling
factor, rather than after. Fixes #2907
2020-08-10 17:28:08 +05:30
Kovid Goyal
1d651270d3 Merge branch 'weechat-hints-kitten' of https://github.com/GermainZ/kitty into master 2020-08-07 21:55:51 +05:30
GermainZ
6ea58fac1a Add link to weechat-hints kitten 2020-08-07 17:38:38 +02:00
Kovid Goyal
c7eb63d4ff Avoid extra layer update if clearing images has no effect 2020-08-07 09:36:28 +05:30
Kovid Goyal
4062365b19 Revert "Adjust cell height automatically for buggy fonts that draw brackets outside the bounding box"
This reverts commit 5555a66638.
Since it does not fix the issue.
2020-08-06 21:20:54 +05:30
Kovid Goyal
7f48a927de Fix image leaving behind a black rectangle when switching away and back to alternate screen
layer_dirty was being reset if remove_images was called more than once
without an intervening call to update_layers.

Fixes #2901
2020-08-06 19:33:28 +05:30
Kovid Goyal
4a740cc556 Cleanup 2020-08-06 18:14:56 +05:30
Kovid Goyal
628b92f20b Speed up is_ignored_char in the common case 2020-08-06 18:05:33 +05:30
Kovid Goyal
8f9616c230 Speed up wcwidth_std in the common case 2020-08-06 17:57:57 +05:30
Kovid Goyal
df475dfde5 Fix indentation 2020-08-06 17:55:02 +05:30
Kovid Goyal
a835b56a51 Speed up is_combining_char() in the common case 2020-08-06 17:45:40 +05:30
Kovid Goyal
5555a66638 Adjust cell height automatically for buggy fonts that draw brackets outside the bounding box
See https://github.com/cormullion/juliamono/issues/14
2020-08-06 14:06:06 +05:30
Kovid Goyal
dad355b932 oops 2020-08-05 11:06:24 +05:30
Kovid Goyal
90a097d45c Ensure beep works even very soon after startup 2020-08-05 11:04:07 +05:30
Kovid Goyal
dba01bc014 Make ringing bell not dependent on having a OSWindow 2020-08-05 10:58:00 +05:30
Kovid Goyal
1d0a3ce998 Merge branch 'master' of https://github.com/ayang64/kitty into master 2020-08-05 07:46:27 +05:30
Ayan George
076ad850bb Declare _glfwPlatformInitJoysticks() to return bool
This patch declares _glfwPlatformInitJoysticks() to return bool which
matches its prototype in glfw/internal.h.
2020-08-04 20:24:35 -04:00
Kovid Goyal
868546abe4 Fix #2896 2020-08-03 22:14:13 +05:30
Kovid Goyal
6b421fabd1 Wayland: Fix a typo in the previous release that broke reading mouse cursor size
Fixes #2895
2020-08-03 17:08:08 +05:30
Kovid Goyal
ab3c9658bd Add hunter to integrations 2020-08-03 09:05:04 +05:30
Kovid Goyal
8c7886d429 Add a changelog entry for the last PR 2020-08-03 09:00:19 +05:30
Kovid Goyal
110a3e7e0f Merge branch 'master' of https://github.com/jaeheum/kitty into master 2020-08-03 08:58:24 +05:30
Jay Han
ee0b70f51b allow users to choose hint colors 2020-08-02 15:33:17 -07:00
Kovid Goyal
df48061a8f Merge branch 'master' of https://github.com/wengxt/kitty into master 2020-08-01 07:30:09 +05:30
Weng Xuetian
b1387d50f0 Register to bus when connect to ibus.
This is required when the actual dbus is not from ibus but session bus.
This may happen when other run a shim layer for ibus, or ibus is
running with flatpak mode.
2020-07-31 17:54:10 -07:00
Kovid Goyal
73dd218a95 Merge branch 'with-suppress' of https://github.com/Luflosi/kitty into master 2020-07-31 21:36:02 +05:30
Luflosi
ee4cedcc81 Use "with suppress()" to suppress a python exception 2020-07-31 18:00:01 +02:00
Kovid Goyal
d1b2f9c6a9 Dont use SystemExit if kitty exe not found 2020-07-31 20:03:04 +05:30
Kovid Goyal
40dee44978 Merge branch 'kitty-exe-error-message' of https://github.com/Luflosi/kitty into master 2020-07-31 20:02:15 +05:30
Luflosi
db04c70d4a Let kitty_exe() raise an error message if kitty could not be found 2020-07-31 15:46:47 +02:00
Kovid Goyal
7401c6ac97 Merge branch 'dryer' of https://github.com/Luflosi/kitty into master 2020-07-31 16:38:23 +05:30
Luflosi
f7a1222cc3 DRYer 2020-07-30 22:11:41 +02:00
Kovid Goyal
069f09c43c Move wlc destructor to just before the functions are unloaded 2020-07-29 17:18:03 +05:30
Kovid Goyal
35f821f6a1 Ensure wl_cursor_theme_destroy is called before it is released
Fix #2884
2020-07-29 17:13:00 +05:30
Kovid Goyal
67f60847ee Fix the spawn test in the bypy environment
Required the launcher to be built since the pre-built
one was being used. Fixes #2881
2020-07-29 15:11:35 +05:30
Kovid Goyal
65f6b142a5 typo 2020-07-28 15:22:14 +05:30
30 changed files with 286 additions and 155 deletions

View File

@@ -7,6 +7,7 @@ import re
import shutil
import subprocess
import sys
from contextlib import suppress
from bypy.constants import (
LIBDIR, PREFIX, PYTHON, SRC as KITTY_DIR, ismacos, worker_env
@@ -47,6 +48,16 @@ def build_c_extensions(ext_dir, args):
shutil.copytree(
KITTY_DIR, writeable_src_dir, symlinks=True,
ignore=shutil.ignore_patterns('b', 'build', 'dist', '*_commands.json', '*.o'))
# Build the launcher as it is needed for the spawn test
with suppress(FileNotFoundError):
os.remove(os.path.join(writeable_src_dir, 'kitty', 'launcher', 'kitty'))
if run(PYTHON, 'setup.py', 'build-launcher', cwd=writeable_src_dir) != 0:
print('Building of kitty launcher failed', file=sys.stderr)
os.chdir(KITTY_DIR)
run_shell()
raise SystemExit('Building of kitty launcher failed')
cmd = [PYTHON, 'setup.py']
bundle = 'macos-freeze' if ismacos else 'linux-freeze'
cmd.append(bundle)

View File

@@ -4,6 +4,27 @@ Changelog
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
To update |kitty|, :doc:`follow the instructions <binary>`.
0.18.3 [2020-08-11]
-------------------
- hints kitten: Allow customizing hint colors (:pull:`2894`)
- Wayland: Fix a typo in the previous release that broke reading mouse cursor size (:iss:`2895`)
- Fix a regression in the previous release that could cause an exception during
startup in rare circumstances (:iss:`2896`)
- Fix image leaving behind a black rectangle when switch away and back to
alternate screen (:iss:`2901`)
- Fix one pixel mis-alignment of rounded corners when either the cell
dimensions or the thickness of the line is an odd number of pixels
(:iss:`2907`)
- Fix a regression that broke specifying OS window size in the session file
(:iss:`2908`)
0.18.2 [2020-07-28]
--------------------
@@ -25,7 +46,7 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
- When a character from the Unicode Dingbat block is followed by a space, use
the extra space to render a larger version of the character (:iss:`2850`)
- macOS: Fix the LC_TYPE env var being set to UTF-8 on systems in which the
- macOS: Fix the LC_CTYPE env var being set to UTF-8 on systems in which the
language and country code do not form a valid locale (:iss:`1233`)
- macOS: Fix :kbd:`cmd+plus` not changing font size (:iss:`2839`)

View File

@@ -24,6 +24,9 @@ images and other types of documents directly in your terminal, even over SSH.
`nnn <https://github.com/jarun/nnn/>`_
another terminal file manager, with previews of file contents powered by kitty's graphics protocol.
`hunter <https://github.com/rabite0/hunter>`_
another terminal file manager, with previews of file contents powered by kitty's graphics protocol.
`koneko <https://github.com/twenty5151/koneko>`_
browse images from the pixiv artist community directly in kitty
@@ -109,3 +112,7 @@ Miscellaneous
`kitti3 <https://github.com/LandingEllipse/kitti3>`_
allow using kitty as a drop-down terminal under the i3 window manager
`weechat-hints <https://github.com/GermainZ/kitty-weechat-hints>`_
URL hints kitten for WeeChat that works without having to use WeeChat's
raw-mode.

View File

@@ -196,3 +196,7 @@ Kittens created by kitty users
`insert password <https://github.com/kovidgoyal/kitty/issues/1222>`_
Insert a password from a CLI password manager, taking care to only do it at
a password prompt.
`weechat-hints <https://github.com/GermainZ/kitty-weechat-hints>`_
URL hints kitten for WeeChat that works without having to use WeeChat's
raw-mode.

View File

@@ -10,18 +10,18 @@ form::
Where ``<ESC>`` is the byte ``0x1b``. The JSON object has the form::
{
'cmd': "command name",
'version': "kitty version",
'no_response': Optional Boolean,
'payload': <Optional JSON object>,
"cmd": "command name",
"version": <kitty version>,
"no_response": Optional Boolean,
"payload": <Optional JSON object>,
}
The ``version`` above is a string of the form :code:`0.14.2`. If you are developing a
The ``version`` above is an array of the form :code:`[0, 14, 2]`. If you are developing a
standalone client, use the kitty version that you are developing against. Using
a version greater than the version of the kitty instance you are talking to,
will cause a failure.
Set ``no_response`` to True if you don't want a response from kitty.
Set ``no_response`` to ``true`` if you don't want a response from kitty.
The optional payload is a JSON object that is specific to the actual command being sent.
The fields in the object for every command are documented below.

View File

@@ -274,7 +274,9 @@ def category_test(
comment: str,
use_static: bool = False,
extra_chars: Union[FrozenSet[int], Set[int]] = frozenset(),
exclude: Union[Set[int], FrozenSet[int]] = frozenset()
exclude: Union[Set[int], FrozenSet[int]] = frozenset(),
least_check_return: Optional[str] = None,
ascii_range: Optional[str] = None
) -> None:
static = 'static inline ' if use_static else ''
chars: Set[int] = set()
@@ -284,6 +286,11 @@ def category_test(
chars -= exclude
p(f'{static}bool\n{name}(char_type code) {{')
p(f'\t// {comment} ({len(chars)} codepoints)' + ' {{' '{')
if least_check_return is not None:
least = min(chars)
p(f'\tif (LIKELY(code < {least})) return {least_check_return};')
if ascii_range is not None:
p(f'\tif (LIKELY(0x20 <= code && code <= 0x7e)) return {ascii_range};')
p('\tswitch(code) {')
for spec in get_ranges(list(chars)):
write_case(spec, p)
@@ -337,11 +344,15 @@ def gen_ucd() -> None:
{c for c in class_maps if c.startswith('M')},
'M category (marks)',
# See https://github.com/harfbuzz/harfbuzz/issues/169
extra_chars=emoji_skin_tone_modifiers | {zwj}
extra_chars=emoji_skin_tone_modifiers | {zwj},
least_check_return='false'
)
category_test(
'is_ignored_char', p, 'Cc Cf Cs'.split(),
'Control characters and non-characters', extra_chars=non_characters, exclude={zwj})
'Control characters and non-characters',
extra_chars=non_characters, exclude={zwj},
ascii_range='false'
)
category_test('is_word_char', p, {c for c in class_maps if c[0] in 'LN'}, 'L and N categories')
category_test('is_CZ_category', p, cz, 'C and Z categories')
category_test('is_P_category', p, {c for c in class_maps if c[0] == 'P'}, 'P category (punctuation)')
@@ -421,7 +432,7 @@ def gen_names() -> None:
p('}; // }}}\n')
# The trie
p('typedef struct {{ uint32_t children_offset; uint32_t match_offset; }} word_trie;\n')
p('typedef struct { uint32_t children_offset; uint32_t match_offset; } word_trie;\n')
all_trie_nodes: List['TrieNode'] = [] # noqa
class TrieNode:
@@ -482,6 +493,7 @@ def gen_wcwidth() -> None:
with create_header('kitty/wcwidth-std.h') as p:
p('static int\nwcwidth_std(int32_t code) {')
p('\tif (LIKELY(0x20 <= code && code <= 0x7e)) return 1;')
p('\tswitch(code) {')
non_printing = class_maps['Cc'] | class_maps['Cf'] | class_maps['Cs']

2
glfw/ibus_glfw.c vendored
View File

@@ -269,7 +269,7 @@ setup_connection(_GLFWIBUSData *ibus) {
ibus->conn = NULL;
}
debug("Connecting to IBUS daemon @ %s for IME input management\n", ibus->address);
ibus->conn = glfw_dbus_connect_to(ibus->address, "Failed to connect to the IBUS daemon, with error", "ibus", false);
ibus->conn = glfw_dbus_connect_to(ibus->address, "Failed to connect to the IBUS daemon, with error", "ibus", true);
if (!ibus->conn) return false;
free((void*)ibus->input_ctx_path); ibus->input_ctx_path = NULL;
if (!glfw_dbus_call_method_with_reply(

View File

@@ -33,7 +33,7 @@
////// GLFW platform API //////
//////////////////////////////////////////////////////////////////////////
int _glfwPlatformInitJoysticks(void)
bool _glfwPlatformInitJoysticks(void)
{
return true;
}

2
glfw/wl_cursors.c vendored
View File

@@ -16,7 +16,7 @@ pixels_from_scale(int scale) {
const char *env = getenv("XCURSOR_SIZE");
if (env) {
const int retval = atoi(env);
if (retval > 0 && retval < 2048) factor = 32;
if (retval > 0 && retval < 2048) factor = retval;
}
queried_env = true;
}

2
glfw/wl_init.c vendored
View File

@@ -802,6 +802,7 @@ void _glfwPlatformTerminate(void)
glfw_xkb_release(&_glfw.wl.xkb);
glfw_dbus_terminate(&_glfw.wl.dbus);
glfw_wlc_destroy();
if (_glfw.wl.cursor.handle)
{
_glfw_dlclose(_glfw.wl.cursor.handle);
@@ -810,7 +811,6 @@ void _glfwPlatformTerminate(void)
if (_glfw.wl.cursorSurface)
wl_surface_destroy(_glfw.wl.cursorSurface);
glfw_wlc_destroy();
if (_glfw.wl.subcompositor)
wl_subcompositor_destroy(_glfw.wl.subcompositor);
if (_glfw.wl.compositor)

View File

@@ -72,7 +72,7 @@ def decode_hint(x: str, alphabet: str = DEFAULT_HINT_ALPHABET) -> int:
return i
def highlight_mark(m: Mark, text: str, current_input: str, alphabet: str) -> str:
def highlight_mark(m: Mark, text: str, current_input: str, alphabet: str, colors: Dict[str, str]) -> str:
hint = encode_hint(m.index, alphabet)
if current_input and not hint.startswith(current_input):
return faint(text)
@@ -80,19 +80,19 @@ def highlight_mark(m: Mark, text: str, current_input: str, alphabet: str) -> str
text = text[len(hint):]
return styled(
hint,
fg='black',
bg='green',
fg=colors['foreground'],
bg=colors['background'],
bold=True
) + styled(
text, fg='gray', fg_intense=True, bold=True
text, fg=colors['text'], fg_intense=True, bold=True
)
def render(text: str, current_input: str, all_marks: Sequence[Mark], ignore_mark_indices: Set[int], alphabet: str) -> str:
def render(text: str, current_input: str, all_marks: Sequence[Mark], ignore_mark_indices: Set[int], alphabet: str, colors: Dict[str, str]) -> str:
for mark in reversed(all_marks):
if mark.index in ignore_mark_indices:
continue
mtext = highlight_mark(mark, text[mark.start:mark.end], current_input, alphabet)
mtext = highlight_mark(mark, text[mark.start:mark.end], current_input, alphabet, colors)
text = text[:mark.start] + mtext + text[mark.end:]
text = text.replace('\0', '')
@@ -105,6 +105,9 @@ class Hints(Handler):
def __init__(self, text: str, all_marks: Sequence[Mark], index_map: Dict[int, Mark], args: HintsCLIOptions):
self.text, self.index_map = text, index_map
self.alphabet = args.alphabet or DEFAULT_HINT_ALPHABET
self.colors = {'foreground': args.hints_foreground_color,
'background': args.hints_background_color,
'text': args.hints_text_color}
self.all_marks = all_marks
self.ignore_mark_indices: Set[int] = set()
self.args = args
@@ -198,7 +201,7 @@ class Hints(Handler):
def draw_screen(self) -> None:
if self.current_text is None:
self.current_text = render(self.text, self.current_input, self.all_marks, self.ignore_mark_indices, self.alphabet)
self.current_text = render(self.text, self.current_input, self.all_marks, self.ignore_mark_indices, self.alphabet, self.colors)
self.cmd.clear_screen()
self.write(self.current_text)
@@ -513,6 +516,24 @@ type=bool-set
Have the hints increase from top to bottom instead of decreasing from top to bottom.
--hints-foreground-color
default=black
type=str
The foreground color for hints
--hints-background-color
default=green
type=str
The background color for hints
--hints-text-color
default=gray
type=str
The foreground color for text pointed to by the hints
--customize-processing
Name of a python file in the kitty config directory which will be imported to provide
custom implementations for pattern finding and performing actions

View File

@@ -10,8 +10,8 @@ from typing import Callable, Dict, List, Tuple
from kitty.cli import parse_args
from kitty.cli_stub import PanelCLIOptions
from kitty.options_stub import Options
from kitty.constants import is_macos
from kitty.os_window_size import WindowSizeData
OPTIONS = r'''
--lines
@@ -108,7 +108,7 @@ def setup_x11_window(win_id: int) -> None:
func(win_id, window_width, window_height)
def initial_window_size_func(opts: Options, cached_values: Dict) -> Callable[[int, int, float, float, float, float], Tuple[int, int]]:
def initial_window_size_func(opts: WindowSizeData, cached_values: Dict) -> Callable[[int, int, float, float, float, float], Tuple[int, int]]:
from kitty.fast_data_types import glfw_primary_monitor_size, set_smallest_allowed_resize
def initial_window_size(cell_width: int, cell_height: int, dpi_x: float, dpi_y: float, xscale: float, yscale: float) -> Tuple[int, int]:

View File

@@ -1,4 +1,4 @@
// unicode data, built from the unicode standard on: 2020-04-06
// unicode data, built from the unicode standard on: 2020-08-06
// see gen-wcwidth.py
#pragma once
#include "data-types.h"

View File

@@ -19,7 +19,7 @@ from .cli import create_opts, parse_args
from .cli_stub import CLIOptions
from .conf.utils import BadLine, to_cmdline
from .config import (
KeyAction, SubSequenceMap, common_opts_as_dict, initial_window_size_func,
KeyAction, SubSequenceMap, common_opts_as_dict,
prepare_config_file_for_editing
)
from .config_data import MINIMUM_FONT_SIZE
@@ -40,8 +40,9 @@ from .fast_data_types import (
from .keys import get_shortcut, shortcut_matches
from .layout.base import set_layout_options
from .options_stub import Options
from .os_window_size import initial_window_size_func
from .rgb import Color, color_from_int
from .session import Session, create_sessions
from .session import Session, create_sessions, get_os_window_sizing_data
from .tabs import (
SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager
)
@@ -195,11 +196,11 @@ class Boss:
startup_id: Optional[str] = None
) -> int:
if os_window_id is None:
opts_for_size = opts_for_size or getattr(startup_session, 'os_window_size', None) or self.opts
size_data = get_os_window_sizing_data(opts_for_size or self.opts, startup_session)
wclass = wclass or getattr(startup_session, 'os_window_class', None) or self.args.cls or appname
with startup_notification_handler(do_notify=startup_id is not None, startup_id=startup_id) as pre_show_callback:
os_window_id = create_os_window(
initial_window_size_func(opts_for_size, self.cached_values),
initial_window_size_func(size_data, self.cached_values),
pre_show_callback,
self.args.title or appname, wname or self.args.name or wclass, wclass)
tm = TabManager(os_window_id, self.opts, self.args, startup_session)

View File

@@ -537,6 +537,11 @@ cocoa_hide_titlebar(void *w)
} // autoreleasepool
}
void
cocoa_system_beep(void) {
NSBeep();
}
static PyMethodDef module_methods[] = {
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
{"cocoa_set_new_window_trigger", (PyCFunction)cocoa_set_new_window_trigger, METH_VARARGS, ""},

View File

@@ -23,7 +23,7 @@ from .config_data import all_options, parse_mods, type_convert
from .constants import cache_dir, defconf, is_macos
from .key_names import get_key_name_lookup, key_name_aliases
from .options_stub import Options as OptionsStub
from .typing import EdgeLiteral, TypedDict
from .typing import TypedDict
from .utils import expandvars, log_error
KeySpec = Tuple[int, bool, int]
@@ -718,51 +718,6 @@ def cached_values_for(name: str) -> Generator[Dict, None, None]:
err))
def initial_window_size_func(opts: OptionsStub, cached_values: Dict) -> Callable[[int, int, float, float, float, float], Tuple[int, int]]:
if 'window-size' in cached_values and opts.remember_window_size:
ws = cached_values['window-size']
try:
w, h = map(int, ws)
def initial_window_size(*a: Any) -> Tuple[int, int]:
return w, h
return initial_window_size
except Exception:
log_error('Invalid cached window size, ignoring')
w, w_unit = opts.initial_window_width
h, h_unit = opts.initial_window_height
def get_window_size(cell_width: int, cell_height: int, dpi_x: float, dpi_y: float, xscale: float, yscale: float) -> Tuple[int, int]:
if not is_macos:
# scaling is not needed on Wayland, but is needed on macOS. Not
# sure about X11.
xscale = yscale = 1
def effective_margin(which: EdgeLiteral) -> float:
ans: float = getattr(opts.single_window_margin_width, which)
if ans < 0:
ans = getattr(opts.window_margin_width, which)
return ans
if w_unit == 'cells':
spacing = effective_margin('left') + effective_margin('right')
spacing += opts.window_padding_width.left + opts.window_padding_width.right
width = cell_width * w / xscale + (dpi_x / 72) * spacing + 1
else:
width = w
if h_unit == 'cells':
spacing = effective_margin('top') + effective_margin('bottom')
spacing += opts.window_padding_width.top + opts.window_padding_width.bottom
height = cell_height * h / yscale + (dpi_y / 72) * spacing + 1
else:
height = h
return int(width), int(height)
return get_window_size
def commented_out_default_config() -> str:
ans = []
for line in as_conf_file(all_options.values()):

View File

@@ -20,7 +20,7 @@ class Version(NamedTuple):
appname: str = 'kitty'
version: Version = Version(0, 18, 2)
version: Version = Version(0, 18, 3)
str_version: str = '.'.join(map(str, version))
_plat = sys.platform.lower()
is_macos: bool = 'darwin' in _plat
@@ -64,16 +64,16 @@ class WindowGeometry(NamedTuple):
def kitty_exe() -> str:
rpath = sys._xoptions.get('bundle_exe_dir')
if not rpath:
items = filter(None, os.environ.get('PATH', '').split(os.pathsep))
items = os.environ.get('PATH', '').split(os.pathsep) + [os.path.join(base, 'launcher')]
seen: Set[str] = set()
for candidate in items:
for candidate in filter(None, items):
if candidate not in seen:
seen.add(candidate)
if os.access(os.path.join(candidate, 'kitty'), os.X_OK):
rpath = candidate
break
else:
rpath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'launcher')
raise RuntimeError('kitty binary not found')
return os.path.join(rpath, 'kitty')
@@ -146,7 +146,7 @@ def wakeup() -> None:
b.child_monitor.wakeup()
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
base_dir = os.path.dirname(base)
terminfo_dir = os.path.join(base_dir, 'terminfo')
logo_data_file = os.path.join(base_dir, 'logo', 'kitty.rgba')
logo_png_file = os.path.join(base_dir, 'logo', 'kitty.png')

2
kitty/emoji.h generated
View File

@@ -1,4 +1,4 @@
// unicode data, built from the unicode standard on: 2020-04-06
// unicode data, built from the unicode standard on: 2020-08-06
// see gen-wcwidth.py
#pragma once
#include "data-types.h"

View File

@@ -364,20 +364,19 @@ def D(buf: BufType, width: int, height: int, left: bool = True) -> None:
buf[offset + dest_x] = mbuf[offset + src_x]
def draw_parametrized_curve(buf: BufType, width: int, height: int, thickness_in_pixels: int, xfunc: BezierFunc, yfunc: BezierFunc) -> None:
def draw_parametrized_curve(buf: BufType, width: int, height: int, delta: int, extra: int, xfunc: BezierFunc, yfunc: BezierFunc) -> None:
num_samples = height*4
seen = set()
delta, extra = divmod(thickness_in_pixels, 2)
for i in range(num_samples + 1):
t = (i / num_samples)
p = x_p, y_p = int(xfunc(t)), int(yfunc(t))
if p in seen:
continue
seen.add(p)
for y in range(int(y_p) - delta, int(y_p) + delta + extra):
for y in range(y_p - delta, y_p + delta + extra):
if 0 <= y < height:
offset = y * width
for x in range(int(x_p) - delta, int(x_p) + delta + extra):
for x in range(x_p - delta, x_p + delta + extra):
if 0 <= x < width:
pos = offset + x
buf[pos] = min(255, buf[pos] + 255)
@@ -386,29 +385,31 @@ def draw_parametrized_curve(buf: BufType, width: int, height: int, thickness_in_
@supersampled()
def rounded_corner(buf: BufType, width: int, height: int, level: int = 1, which: str = '') -> None:
supersample_factor = getattr(buf, 'supersample_factor')
thickness_in_pixels = thickness(level) * supersample_factor
delta, extra = divmod(thickness(level), 2)
hw = ((width / supersample_factor) // 2) * supersample_factor
hh = ((height / supersample_factor) // 2) * supersample_factor
if which == '':
start = width // 2, height - 1
end = width - 1, height // 2
c1 = width // 2, int(0.75 * height)
c2 = width // 2, height // 2 + 1
start = hw, height - 1
end = width - 1, hh
c1 = hw, int(0.75 * height)
c2 = hw, hh + 1
elif which == '':
start = 0, height // 2
end = width // 2, height - 1
c1 = width // 2, height // 2 + 1
c2 = width // 2, int(0.75 * height)
start = 0, hh
end = hw, height - 1
c1 = hw, hh + 1
c2 = hw, int(0.75 * height)
elif which == '':
start = width // 2, 0
end = width - 1, height // 2
c1 = width // 2, int(0.25 * height)
c2 = width // 2 - 1, height // 2 - 1
end = width - 1, hh
c1 = hw, int(0.25 * height)
c2 = hw, hh - 1
elif which == '':
start = 0, height // 2
end = width // 2, 0
c1 = width // 2 - 1, height // 2 - 1
c2 = width // 2, int(0.25 * height)
start = 0, hh
end = hw, 0
c1 = hw, hh - 1
c2 = hw, int(0.25 * height)
xfunc, yfunc = cubic_bezier(start, end, c1, c2)
draw_parametrized_curve(buf, width, height, thickness_in_pixels, xfunc, yfunc)
draw_parametrized_curve(buf, width, height, delta * supersample_factor, extra * supersample_factor, xfunc, yfunc)
def half_dhline(buf: BufType, width: int, height: int, level: int = 1, which: str = 'left', only: Optional[str] = None) -> Tuple[int, int]:
@@ -686,7 +687,7 @@ t, f = 1, 3
for start in '┌┐└┘':
for i, (hlevel, vlevel) in enumerate(((t, t), (f, t), (t, f), (f, f))):
box_chars[chr(ord(start) + i)] = [p(corner, which=start, hlevel=hlevel, vlevel=vlevel)]
for ch in '╭╮':
for ch in '╭╮╰':
box_chars[ch] = [p(rounded_corner, which=ch)]
for i, (a_, b_, c_, d_) in enumerate((

View File

@@ -15,6 +15,7 @@ extern long cocoa_window_number(void *w);
extern void cocoa_create_global_menu(void);
extern void cocoa_hide_window_title(void *w);
extern void cocoa_hide_titlebar(void *w);
extern void cocoa_system_beep(void);
extern void cocoa_set_activation_policy(bool);
extern void cocoa_set_titlebar_color(void *w, color_type color);
extern bool cocoa_alt_option_key_pressed(unsigned long);
@@ -888,16 +889,14 @@ get_clipboard_string(PYNOARG) {
return Py_BuildValue("s", "");
}
void
ring_audio_bell(OSWindow *w UNUSED) {
static void
ring_audio_bell(void) {
static monotonic_t last_bell_at = -1;
monotonic_t now = monotonic();
if (now - last_bell_at <= ms_to_monotonic_t(100ll)) return;
if (last_bell_at >= 0 && now - last_bell_at <= ms_to_monotonic_t(100ll)) return;
last_bell_at = now;
#ifdef __APPLE__
if (w->handle) {
glfwWindowBell(w->handle);
}
cocoa_system_beep();
#else
play_canberra_sound("bell", "kitty bell");
#endif
@@ -905,8 +904,7 @@ ring_audio_bell(OSWindow *w UNUSED) {
static PyObject*
ring_bell(PYNOARG) {
OSWindow *w = current_os_window();
ring_audio_bell(w);
ring_audio_bell();
Py_RETURN_NONE;
}
@@ -960,7 +958,7 @@ void
request_window_attention(id_type kitty_window_id, bool audio_bell) {
OSWindow *w = os_window_for_kitty_window(kitty_window_id);
if (w) {
if (audio_bell) ring_audio_bell(w);
if (audio_bell) ring_audio_bell();
if (OPT(window_alert_on_bell)) glfwRequestWindowAttention(w->handle);
glfwPostEmptyEvent();
}

View File

@@ -645,20 +645,32 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scree
// Image lifetime/scrolling {{{
static inline void
filter_refs(GraphicsManager *self, const void* data, bool free_images, bool (*filter_func)(ImageRef*, Image*, const void*, CellPixelSize), CellPixelSize cell) {
self->layers_dirty = self->image_count > 0;
filter_refs(GraphicsManager *self, const void* data, bool free_images, bool (*filter_func)(const ImageRef*, Image*, const void*, CellPixelSize), CellPixelSize cell) {
for (size_t i = self->image_count; i-- > 0;) {
Image *img = self->images + i;
for (size_t j = img->refcnt; j-- > 0;) {
ImageRef *ref = img->refs + j;
if (filter_func(ref, img, data, cell)) {
remove_i_from_array(img->refs, j, img->refcnt);
self->layers_dirty = true;
}
}
if (img->refcnt == 0 && (free_images || img->client_id == 0)) remove_image(self, i);
}
}
static inline void
modify_refs(GraphicsManager *self, const void* data, bool free_images, bool (*filter_func)(ImageRef*, Image*, const void*, CellPixelSize), CellPixelSize cell) {
for (size_t i = self->image_count; i-- > 0;) {
Image *img = self->images + i;
for (size_t j = img->refcnt; j-- > 0;) {
if (filter_func(img->refs + j, img, data, cell)) remove_i_from_array(img->refs, j, img->refcnt);
}
if (img->refcnt == 0 && (free_images || img->client_id == 0)) remove_image(self, i);
}
}
static inline bool
scroll_filter_func(ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {
ScrollData *d = (ScrollData*)data;
@@ -667,12 +679,12 @@ scroll_filter_func(ImageRef *ref, Image UNUSED *img, const void *data, CellPixel
}
static inline bool
ref_within_region(ImageRef *ref, index_type margin_top, index_type margin_bottom) {
ref_within_region(const ImageRef *ref, index_type margin_top, index_type margin_bottom) {
return ref->start_row >= (int32_t)margin_top && ref->start_row + ref->effective_num_rows <= margin_bottom;
}
static inline bool
ref_outside_region(ImageRef *ref, index_type margin_top, index_type margin_bottom) {
ref_outside_region(const ImageRef *ref, index_type margin_top, index_type margin_bottom) {
return ref->start_row + ref->effective_num_rows <= margin_top || ref->start_row > (int32_t)margin_bottom;
}
@@ -709,16 +721,19 @@ scroll_filter_margins_func(ImageRef* ref, Image* img, const void* data, CellPixe
void
grman_scroll_images(GraphicsManager *self, const ScrollData *data, CellPixelSize cell) {
filter_refs(self, data, true, data->has_margins ? scroll_filter_margins_func : scroll_filter_func, cell);
if (self->image_count) {
self->layers_dirty = true;
modify_refs(self, data, true, data->has_margins ? scroll_filter_margins_func : scroll_filter_func, cell);
}
}
static inline bool
clear_filter_func(ImageRef *ref, Image UNUSED *img, const void UNUSED *data, CellPixelSize cell UNUSED) {
clear_filter_func(const ImageRef *ref, Image UNUSED *img, const void UNUSED *data, CellPixelSize cell UNUSED) {
return ref->start_row + (int32_t)ref->effective_num_rows > 0;
}
static inline bool
clear_all_filter_func(ImageRef *ref UNUSED, Image UNUSED *img, const void UNUSED *data, CellPixelSize cell UNUSED) {
clear_all_filter_func(const ImageRef *ref UNUSED, Image UNUSED *img, const void UNUSED *data, CellPixelSize cell UNUSED) {
return true;
}
@@ -728,37 +743,37 @@ grman_clear(GraphicsManager *self, bool all, CellPixelSize cell) {
}
static inline bool
id_filter_func(ImageRef UNUSED *ref, Image *img, const void *data, CellPixelSize cell UNUSED) {
id_filter_func(const ImageRef UNUSED *ref, Image *img, const void *data, CellPixelSize cell UNUSED) {
uint32_t iid = *(uint32_t*)data;
return img->client_id == iid;
}
static inline bool
x_filter_func(ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {
x_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {
const GraphicsCommand *g = data;
return ref->start_column <= (int32_t)g->x_offset - 1 && ((int32_t)g->x_offset - 1) < ((int32_t)(ref->start_column + ref->effective_num_cols));
}
static inline bool
y_filter_func(ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {
y_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {
const GraphicsCommand *g = data;
return ref->start_row <= (int32_t)g->y_offset - 1 && ((int32_t)(g->y_offset - 1 < ref->start_row + ref->effective_num_rows));
}
static inline bool
z_filter_func(ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {
z_filter_func(const ImageRef *ref, Image UNUSED *img, const void *data, CellPixelSize cell UNUSED) {
const GraphicsCommand *g = data;
return ref->z_index == g->z_index;
}
static inline bool
point_filter_func(ImageRef *ref, Image *img, const void *data, CellPixelSize cell) {
point_filter_func(const ImageRef *ref, Image *img, const void *data, CellPixelSize cell) {
return x_filter_func(ref, img, data, cell) && y_filter_func(ref, img, data, cell);
}
static inline bool
point3d_filter_func(ImageRef *ref, Image *img, const void *data, CellPixelSize cell) {
point3d_filter_func(const ImageRef *ref, Image *img, const void *data, CellPixelSize cell) {
return z_filter_func(ref, img, data, cell) && point_filter_func(ref, img, data, cell);
}

View File

@@ -559,13 +559,14 @@ line_get_char(Line *self, index_type at) {
void
line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Cursor *cursor, bool UNUSED is_second) {
GPUCell *g = self->gpu_cells + at;
if (cursor == NULL) {
self->gpu_cells[at].attrs = (self->gpu_cells[at].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width;
g->attrs = (self->gpu_cells[at].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width;
} else {
self->gpu_cells[at].attrs = CURSOR_TO_ATTRS(cursor, width & WIDTH_MASK);
self->gpu_cells[at].fg = (cursor->fg & COL_MASK);
self->gpu_cells[at].bg = (cursor->bg & COL_MASK);
self->gpu_cells[at].decoration_fg = cursor->decoration_fg & COL_MASK;
g->attrs = CURSOR_TO_ATTRS(cursor, width & WIDTH_MASK);
g->fg = cursor->fg & COL_MASK;
g->bg = cursor->bg & COL_MASK;
g->decoration_fg = cursor->decoration_fg & COL_MASK;
}
self->cpu_cells[at].ch = ch;
memset(self->cpu_cells[at].cc_idx, 0, sizeof(self->cpu_cells[at].cc_idx));

View File

@@ -15,7 +15,7 @@ from .child import set_default_env
from .cli import create_opts, parse_args
from .cli_stub import CLIOptions
from .conf.utils import BadLine
from .config import cached_values_for, initial_window_size_func
from .config import cached_values_for
from .constants import (
appname, beam_cursor_data_file, config_dir, glfw_path, is_macos,
is_wayland, kitty_exe, logo_data_file, running_in_kitty
@@ -28,6 +28,8 @@ from .fast_data_types import (
from .fonts.box_drawing import set_scale
from .fonts.render import set_font_family
from .options_stub import Options as OptionsStub
from .os_window_size import initial_window_size_func
from .session import get_os_window_sizing_data
from .utils import (
detach, expandvars, log_error, read_shell_environment, single_instance,
startup_notification_handler, unix_socket_paths
@@ -129,7 +131,7 @@ def _run_app(opts: OptionsStub, args: CLIOptions, bad_lines: Sequence[BadLine] =
with cached_values_for(run_app.cached_values_name) as cached_values:
with startup_notification_handler(extra_callback=run_app.first_window_callback) as pre_show_callback:
window_id = create_os_window(
run_app.initial_window_size_func(opts, cached_values),
run_app.initial_window_size_func(get_os_window_sizing_data(opts), cached_values),
pre_show_callback,
args.title or appname, args.name or args.cls or appname,
args.cls or appname, load_all_shaders)

74
kitty/os_window_size.py Normal file
View File

@@ -0,0 +1,74 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from typing import Any, Callable, Dict, NamedTuple, Tuple
from .constants import FloatEdges, is_macos
from .typing import EdgeLiteral
from .utils import log_error
class WindowSize(NamedTuple):
size: int
unit: str
class WindowSizes(NamedTuple):
width: WindowSize
height: WindowSize
class WindowSizeData(NamedTuple):
initial_window_sizes: WindowSizes
remember_window_size: bool
single_window_margin_width: FloatEdges
window_margin_width: FloatEdges
window_padding_width: FloatEdges
def initial_window_size_func(opts: WindowSizeData, cached_values: Dict) -> Callable[[int, int, float, float, float, float], Tuple[int, int]]:
if 'window-size' in cached_values and opts.remember_window_size:
ws = cached_values['window-size']
try:
w, h = map(int, ws)
def initial_window_size(*a: Any) -> Tuple[int, int]:
return w, h
return initial_window_size
except Exception:
log_error('Invalid cached window size, ignoring')
w, w_unit = opts.initial_window_sizes.width
h, h_unit = opts.initial_window_sizes.height
def get_window_size(cell_width: int, cell_height: int, dpi_x: float, dpi_y: float, xscale: float, yscale: float) -> Tuple[int, int]:
if not is_macos:
# scaling is not needed on Wayland, but is needed on macOS. Not
# sure about X11.
xscale = yscale = 1
def effective_margin(which: EdgeLiteral) -> float:
ans: float = getattr(opts.single_window_margin_width, which)
if ans < 0:
ans = getattr(opts.window_margin_width, which)
return ans
if w_unit == 'cells':
spacing = effective_margin('left') + effective_margin('right')
spacing += opts.window_padding_width.left + opts.window_padding_width.right
width = cell_width * w / xscale + (dpi_x / 72) * spacing + 1
else:
width = w
if h_unit == 'cells':
spacing = effective_margin('top') + effective_margin('bottom')
spacing += opts.window_padding_width.top + opts.window_padding_width.bottom
height = cell_height * h / yscale + (dpi_y / 72) * spacing + 1
else:
height = h
return int(width), int(height)
return get_window_size

View File

@@ -4,24 +4,24 @@
import shlex
import sys
from typing import Generator, List, NamedTuple, Optional, Tuple, Union
from typing import Generator, List, Optional, Union
from .cli_stub import CLIOptions
from .config_data import to_layout_names
from .constants import FloatEdges, kitty_exe
from .constants import kitty_exe
from .layout.interface import all_layouts
from .options_stub import Options
from .os_window_size import WindowSize, WindowSizeData, WindowSizes
from .typing import SpecialWindowInstance
from .utils import log_error, resolved_shell
class WindowSizeOpts(NamedTuple):
initial_window_width: Tuple[int, str]
initial_window_height: Tuple[int, str]
window_margin_width: FloatEdges
window_padding_width: FloatEdges
remember_window_size: bool
def get_os_window_sizing_data(opts: Options, session: Optional['Session'] = None) -> WindowSizeData:
if session is None or session.os_window_size is None:
sizes = WindowSizes(WindowSize(*opts.initial_window_width), WindowSize(*opts.initial_window_height))
else:
sizes = session.os_window_size
return WindowSizeData(sizes, opts.remember_window_size, opts.single_window_margin_width, opts.window_margin_width, opts.window_padding_width)
class Tab:
@@ -42,7 +42,7 @@ class Session:
self.tabs: List[Tab] = []
self.active_tab_idx = 0
self.default_title = default_title
self.os_window_size: Optional[WindowSizeOpts] = None
self.os_window_size: Optional[WindowSizes] = None
self.os_window_class: Optional[str] = None
def add_tab(self, opts: Options, name: str = '') -> None:
@@ -124,7 +124,7 @@ def parse_session(raw: str, opts: Options, default_title: Optional[str] = None)
elif cmd == 'os_window_size':
from kitty.config_data import window_size
w, h = map(window_size, rest.split(maxsplit=1))
ans.os_window_size = WindowSizeOpts(w, h, opts.window_margin_width, opts.window_padding_width, False)
ans.os_window_size = WindowSizes(WindowSize(*w), WindowSize(*h))
elif cmd == 'os_window_class':
ans.os_window_class = rest
else:

4
kitty/unicode-data.c generated
View File

@@ -1,4 +1,4 @@
// unicode data, built from the unicode standard on: 2020-04-06
// unicode data, built from the unicode standard on: 2020-08-06
// see gen-wcwidth.py
#include "data-types.h"
@@ -8,6 +8,7 @@ START_ALLOW_CASE_RANGE
bool
is_combining_char(char_type code) {
// M category (marks) (2301 codepoints) {{{
if (LIKELY(code < 768)) return false;
switch(code) {
case 0x300 ... 0x36f:
return true;
@@ -601,6 +602,7 @@ is_combining_char(char_type code) {
bool
is_ignored_char(char_type code) {
// Control characters and non-characters (2339 codepoints) {{{
if (LIKELY(0x20 <= code && code <= 0x7e)) return false;
switch(code) {
case 0x0 ... 0x1f:
return true;

3
kitty/wcwidth-std.h generated
View File

@@ -1,4 +1,4 @@
// unicode data, built from the unicode standard on: 2020-04-06
// unicode data, built from the unicode standard on: 2020-08-06
// see gen-wcwidth.py
#pragma once
#include "data-types.h"
@@ -7,6 +7,7 @@ START_ALLOW_CASE_RANGE
static int
wcwidth_std(int32_t code) {
if (LIKELY(0x20 <= code && code <= 0x7e)) return 1;
switch(code) {
// Flags (26 codepoints) {{{
case 0x1f1e6 ... 0x1f1ff:

View File

@@ -380,7 +380,6 @@ class Window:
if self.destroyed:
return
if self.needs_layout or new_geometry.xnum != self.screen.columns or new_geometry.ynum != self.screen.lines:
boss = get_boss()
self.screen.resize(new_geometry.ynum, new_geometry.xnum)
sg = self.update_position(new_geometry)
self.needs_layout = False
@@ -391,7 +390,7 @@ class Window:
self.screen.lines, self.screen.columns,
max(0, new_geometry.right - new_geometry.left), max(0, new_geometry.bottom - new_geometry.top))
if current_pty_size != self.last_reported_pty_size:
boss.child_monitor.resize_pty(self.id, *current_pty_size)
get_boss().child_monitor.resize_pty(self.id, *current_pty_size)
if not self.pty_resized_once:
self.pty_resized_once = True
self.child.mark_terminal_ready()

View File

@@ -3,13 +3,8 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
import sys
import unittest
from . import BaseTest
is32bit = sys.maxsize <= (1 << 32)
class TestTUI(BaseTest):
@@ -48,7 +43,6 @@ class TestTUI(BaseTest):
le.backspace()
self.assertTrue(le.pending_bell)
@unittest.skipIf(is32bit, 'Fails for some unknown reason on 32bit builds')
def test_multiprocessing_spawn(self):
from kitty.multiprocessing import test_spawn
test_spawn()

View File

@@ -696,13 +696,17 @@ def compile_kittens(compilation_database: CompilationDatabase) -> None:
kenv, dest, compilation_database, sources, all_headers + ['kitty/data-types.h'])
def build(args: Options, native_optimizations: bool = True) -> None:
def init_env_from_args(args: Options, native_optimizations: bool = False) -> None:
global env
env = init_env(
args.debug, args.sanitize, native_optimizations, args.profile,
args.egl_library, args.startup_notification_library, args.canberra_library,
args.extra_logging
)
def build(args: Options, native_optimizations: bool = True) -> None:
init_env_from_args(args, native_optimizations)
sources, headers = find_c_files()
compile_c_extension(
kitty_env(), 'kitty/fast_data_types', args.compilation_database, sources, headers
@@ -1041,7 +1045,7 @@ def option_parser() -> argparse.ArgumentParser: # {{{
'action',
nargs='?',
default=Options.action,
choices='build test linux-package kitty.app linux-freeze macos-freeze clean'.split(),
choices='build test linux-package kitty.app linux-freeze macos-freeze build-launcher clean'.split(),
help='Action to perform (default is build)'
)
p.add_argument(
@@ -1156,6 +1160,9 @@ def main() -> None:
create_minimal_macos_bundle(args, launcher_dir)
else:
build_launcher(args, launcher_dir=launcher_dir)
elif args.action == 'build-launcher':
init_env_from_args(args, False)
build_launcher(args, launcher_dir=launcher_dir)
elif args.action == 'linux-package':
build(args, native_optimizations=False)
package(args, bundle_type='linux-package')