Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kitty for openSUSE:Factory checked in at 2022-09-05 21:21:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kitty (Old) and /work/SRC/openSUSE:Factory/.kitty.new.2083 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kitty" Mon Sep 5 21:21:44 2022 rev:11 rq:1001194 version:0.26.2 Changes: -------- --- /work/SRC/openSUSE:Factory/kitty/kitty.changes 2022-09-01 22:13:01.152542540 +0200 +++ /work/SRC/openSUSE:Factory/.kitty.new.2083/kitty.changes 2022-09-05 21:21:45.525094366 +0200 @@ -1,0 +2,23 @@ +Mon Sep 5 06:11:31 UTC 2022 - Michael Vetter <mvet...@suse.com> + +- Update to 0.26.2: + * Allow creating overlay-main windows, which are treated as the + active window unlike normal overlays (#5392) + * hints kitten: Allow using The launch command as the program to run, to open the + result in a new kitty tab/window/etc. (#5462) + * hyperlinked_grep kitten: Allow control over which parts of rg output are + hyperlinked (#5428) + * Fix regression in 0.26.0 that caused launching kitty without working STDIO + handles to result in high CPU usage and prewarming failing (#5444) + * The launch command: Allow setting the margin and padding for newly created + windows (#5463) + * hints kitten: hyperlink matching: Fix hints occasionally matching text on + subsequent line as part of hyperlink (#5450) + * Fix a regression in 0.26.0 that broke mapping of native keys whose key codes + did not fit in 21 bits (#5452) + * Wayland: Fix remembering window size not accurate when client side decorations + are present + * Fix an issue where notification identifiers were not sanitized leading to code + execution if the user clicked on a notification popup from a malicious source. + +------------------------------------------------------------------- Old: ---- kitty-0.26.1.tar.gz New: ---- kitty-0.26.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kitty.spec ++++++ --- /var/tmp/diff_new_pack.elkzUj/_old 2022-09-05 21:21:46.093095872 +0200 +++ /var/tmp/diff_new_pack.elkzUj/_new 2022-09-05 21:21:46.097095882 +0200 @@ -19,7 +19,7 @@ # sphinx_copybutton not in Factory %bcond_with docs Name: kitty -Version: 0.26.1 +Version: 0.26.2 Release: 0 Summary: A GPU-based terminal emulator License: GPL-3.0-only ++++++ kitty-0.26.1.tar.gz -> kitty-0.26.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/docs/changelog.rst new/kitty-0.26.2/docs/changelog.rst --- old/kitty-0.26.1/docs/changelog.rst 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/docs/changelog.rst 2022-09-05 07:19:50.000000000 +0200 @@ -35,6 +35,31 @@ Detailed list of changes ------------------------------------- +0.26.2 [2022-09-05] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Allow creating :code:`overlay-main` windows, which are treated as the active window unlike normal overlays (:iss:`5392`) + +- hints kitten: Allow using :doc:`launch` as the program to run, to open the result in a new kitty tab/window/etc. (:iss:`5462`) + +- hyperlinked_grep kitten: Allow control over which parts of ``rg`` output are hyperlinked (:pull:`5428`) + +- Fix regression in 0.26.0 that caused launching kitty without working STDIO handles to result in high CPU usage and prewarming failing (:iss:`5444`) + +- :doc:`/launch`: Allow setting the margin and padding for newly created windows (:iss:`5463`) + +- macOS: Fix regression in 0.26.0 that caused asking the user for a line of input such as for :ac:`set_tab_title` to not work (:iss:`5447`) + +- hints kitten: hyperlink matching: Fix hints occasionally matching text on subsequent line as part of hyperlink (:pull:`5450`) + +- Fix a regression in 0.26.0 that broke mapping of native keys whose key codes did not fit in 21 bits (:iss:`5452`) + +- Wayland: Fix remembering window size not accurate when client side decorations are present + +- Fix an issue where notification identifiers were not sanitized leading to + code execution if the user clicked on a notification popup from a malicious + source. Thanks to Carter Sande for discovering this vulnerability. + 0.26.1 [2022-08-30] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/docs/faq.rst new/kitty-0.26.2/docs/faq.rst --- old/kitty-0.26.1/docs/faq.rst 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/docs/faq.rst 2022-09-05 07:19:50.000000000 +0200 @@ -312,7 +312,11 @@ Then press the key you want to emulate. Note that this kitten will only show keys that actually reach the terminal program, in particular, keys mapped to actions in kitty will not be shown. To check those first map them to -:ac:`no_op`. +:ac:`no_op`. You can also start a kitty instance without any shortcuts to +interfere:: + + kitty -o clear_all_shortcuts=yes kitty +kitten show_key + How do I open a new window or tab with the same working directory as the current window? -------------------------------------------------------------------------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/docs/glossary.rst new/kitty-0.26.2/docs/glossary.rst --- old/kitty-0.26.1/docs/glossary.rst 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/docs/glossary.rst 2022-09-05 07:19:50.000000000 +0200 @@ -32,7 +32,12 @@ top of an existing kitty window, entirely covering it. Overlays are used throughout kitty, for example, to display the :ref:`the scrollback buffer <scrollback>`, to display :doc:`hints </kittens/hints>`, for :doc:`unicode input - </kittens/unicode_input>` etc. + </kittens/unicode_input>` etc. Normal overlays are meant for short + duration popups and so are not considered the :italic:`active window` + when determining the current working directory or getting input text for + kittens, launch commands, etc. To create an overlay considered as a + :italic:`main window` use the :code:`overlay-main` argument to + :doc:`launch`. hyperlinks Terminals can have hyperlinks, just like the internet. In kitty you can diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/docs/kittens/hyperlinked_grep.rst new/kitty-0.26.2/docs/kittens/hyperlinked_grep.rst --- old/kitty-0.26.1/docs/kittens/hyperlinked_grep.rst 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/docs/kittens/hyperlinked_grep.rst 2022-09-05 07:19:50.000000000 +0200 @@ -74,6 +74,16 @@ To learn more about kitty's powerful framework for customizing URL click actions, see :doc:`here </open_actions>`. +By default, this kitten adds hyperlinks for several parts of ripgrep output: +the per-file header, match context lines, and match lines. You can control +which items are linked with a :command:`--kitten hyperlink` flag. For example, +:command:`--kitten hyperlink=matching_lines` will only add hyperlinks to the +match lines. :command:`--kitten hyperlink=file_headers,context_lines` will link +file headers and context lines but not match lines. :command:`--kitten +hyperlink=none` will cause the command line to be passed to directly to +:command:`rg` so no hyperlinking will be performed. :command:`--kitten +hyperlink` may be specified multiple times. + Hopefully, someday this functionality will make it into some `upstream grep <https://github.com/BurntSushi/ripgrep/issues/665>`__ program directly removing the need for this kitten. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/glfw/wl_window.c new/kitty-0.26.2/glfw/wl_window.c --- old/kitty-0.26.1/glfw/wl_window.c 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/glfw/wl_window.c 2022-09-05 07:19:50.000000000 +0200 @@ -998,7 +998,7 @@ if (window->decorated && !window->monitor && !window->wl.decorations.serverSide) { if (top) - *top = window->wl.decorations.metrics.top; + *top = window->wl.decorations.metrics.top - window->wl.decorations.metrics.visible_titlebar_height; if (left) *left = window->wl.decorations.metrics.width; if (right) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kittens/ask/main.py new/kitty-0.26.2/kittens/ask/main.py --- old/kitty-0.26.1/kittens/ask/main.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kittens/ask/main.py 2022-09-05 07:19:50.000000000 +0200 @@ -158,14 +158,17 @@ class Password(Handler): - def __init__(self, cli_opts: AskCLIOptions, prompt: str) -> None: + def __init__(self, cli_opts: AskCLIOptions, prompt: str, is_password: bool = True, initial_text: str = '') -> None: self.cli_opts = cli_opts self.prompt = prompt + self.initial_text = initial_text from kittens.tui.line_edit import LineEdit - self.line_edit = LineEdit(is_password=True) + self.line_edit = LineEdit(is_password=is_password) def initialize(self) -> None: self.cmd.set_cursor_shape('beam') + if self.initial_text: + self.line_edit.on_text(self.initial_text, True) self.draw_screen() @Handler.atomic_update @@ -205,7 +208,7 @@ return '' -class Choose(Handler): +class Choose(Handler): # {{{ mouse_tracking = MouseTracking.buttons_only def __init__(self, cli_opts: AskCLIOptions) -> None: @@ -441,6 +444,7 @@ def on_interrupt(self) -> None: self.quit_loop(1) on_eot = on_interrupt +# }}} def main(args: List[str]) -> Response: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kittens/hints/main.py new/kitty-0.26.2/kittens/hints/main.py --- old/kitty-0.26.1/kittens/hints/main.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kittens/hints/main.py 2022-09-05 07:19:50.000000000 +0200 @@ -16,7 +16,7 @@ from kitty.cli import parse_args from kitty.cli_stub import HintsCLIOptions from kitty.constants import website_url -from kitty.fast_data_types import get_options, set_clipboard_string +from kitty.fast_data_types import get_options, set_clipboard_string, wcswidth from kitty.key_encoding import KeyEvent from kitty.typing import BossType, KittyCommonOpts from kitty.utils import ( @@ -411,7 +411,10 @@ appended = False for line in full_line.split('\r'): if line: - lines.append(line.ljust(cols, '\0')) + line_sz = wcswidth(line) + if line_sz < cols: + line += '\0' * (cols - line_sz) + lines.append(line) lines.append('\r') appended = True if appended: @@ -536,12 +539,27 @@ --program type=list What program to use to open matched text. Defaults to the default open program -for the operating system. Use a value of :code:`-` to paste the match into the -terminal window instead. A value of :code:`@` will copy the match to the -clipboard. A value of :code:`*` will copy the match to the primary selection -(on systems that support primary selections). A value of :code:`default` will -run the default open program. Can be specified multiple times to run multiple -programs. +for the operating system. Various special values are supported: + +:code:`-` + paste the match into the terminal window. + +:code:`@` + copy the match to the clipboard + +:code:`*` + copy the match to the primary selection (on systems that support primary selections) + +:code:`default` + run the default open program. + +:code:`launch` + run :doc:`/launch` to open the program in a new kitty tab, window, overlay, etc. + For example:: + + --program "launch --type=tab vim" + +Can be specified multiple times to run multiple programs. --type @@ -803,6 +821,7 @@ elif program == '*': set_primary_selection(joined_text()) else: + from kitty.conf.utils import to_cmdline cwd = data['cwd'] program = get_options().open_url_with if program == 'default' else program if text_type == 'hyperlink': @@ -811,12 +830,20 @@ if w is not None: w.open_url(m, hyperlink_id=1, cwd=cwd) else: + launch_args = [] + if isinstance(program, str) and program.startswith('launch '): + launch_args = to_cmdline(program) + launch_args.insert(1, '--cwd=' + cwd) for m, groupdict in zip(matches, groupdicts): if groupdict: m = [] for k, v in groupdict.items(): m.append('{}={}'.format(k, v or '')) - boss.open_url(m, program, cwd=cwd) + if launch_args: + w = boss.window_id_map.get(target_window_id) + boss.call_remote_control(active_window=w, args=tuple(launch_args + ([m] if isinstance(m, str) else m))) + else: + boss.open_url(m, program, cwd=cwd) if __name__ == '__main__': diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kittens/hyperlinked_grep/main.py new/kitty-0.26.2/kittens/hyperlinked_grep/main.py --- old/kitty-0.26.1/kittens/hyperlinked_grep/main.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kittens/hyperlinked_grep/main.py 2022-09-05 07:19:50.000000000 +0200 @@ -20,7 +20,49 @@ def main() -> None: - if not sys.stdout.isatty() and '--pretty' not in sys.argv and '-p' not in sys.argv: + i = 1 + all_link_options = {'matching_lines', 'context_lines', 'file_headers'} + link_options = set() + delegate_to_rg = False + + def parse_link_options(raw: str) -> None: + nonlocal delegate_to_rg + if not raw: + raise SystemExit('Must specify an argument for --kitten option') + p, _, s = raw.partition('=') + if p != 'hyperlink': + raise SystemExit(f'Unknown argument for --kitten: {raw}') + for option in s.split(','): + if option == 'all': + link_options.update(all_link_options) + delegate_to_rg = False + elif option == 'none': + delegate_to_rg = True + link_options.clear() + elif option not in all_link_options: + a = ', '.join(sorted(all_link_options)) + raise SystemExit(f"hyperlink option must be one of all, none, {a}, not '{option}'") + else: + link_options.add(option) + delegate_to_rg = False + + while i < len(sys.argv): + if sys.argv[i] == '--kitten': + next_item = '' if i + 1 >= len(sys.argv) else sys.argv[i + 1] + parse_link_options(next_item) + del sys.argv[i:i+2] + elif sys.argv[i].startswith('--kitten='): + parse_link_options(sys.argv[i][len('--kitten='):]) + del sys.argv[i] + else: + i += 1 + if not link_options: # Default to linking everything if no options given + link_options.update(all_link_options) + link_file_headers = 'file_headers' in link_options + link_context_lines = 'context_lines' in link_options + link_matching_lines = 'matching_lines' in link_options + + if delegate_to_rg or (not sys.stdout.isatty() and '--pretty' not in sys.argv and '-p' not in sys.argv): os.execlp('rg', 'rg', *sys.argv[1:]) cmdline = ['rg', '--pretty', '--with-filename'] + sys.argv[1:] try: @@ -31,7 +73,7 @@ write: Callable[[bytes], None] = cast(Callable[[bytes], None], sys.stdout.buffer.write) sgr_pat = re.compile(br'\x1b\[.*?m') osc_pat = re.compile(b'\x1b\\].*?\x1b\\\\') - num_pat = re.compile(br'^(\d+)[:-]') + num_pat = re.compile(br'^(\d+)([:-])') in_result: bytes = b'' hostname = socket.gethostname().encode('utf-8') @@ -43,20 +85,22 @@ if not clean_line: in_result = b'' write(b'\n') - continue - if in_result: + elif in_result: m = num_pat.match(clean_line) if m is not None: - write_hyperlink(write, in_result, line, frag=m.group(1)) - else: - write(line) + is_match_line = m.group(2) == b':' + if (is_match_line and link_matching_lines) or (not is_match_line and link_context_lines): + write_hyperlink(write, in_result, line, frag=m.group(1)) + continue + write(line) else: if line.strip(): path = quote_from_bytes(os.path.abspath(clean_line)).encode('utf-8') in_result = b'file://' + hostname + path - write_hyperlink(write, in_result, line) - else: - write(line) + if link_file_headers: + write_hyperlink(write, in_result, line) + continue + write(line) except KeyboardInterrupt: p.send_signal(signal.SIGINT) except (EOFError, BrokenPipeError): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kittens/remote_file/main.py new/kitty-0.26.2/kittens/remote_file/main.py --- old/kitty-0.26.1/kittens/remote_file/main.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kittens/remote_file/main.py 2022-09-05 07:19:50.000000000 +0200 @@ -273,9 +273,9 @@ ) print('Relative paths will be resolved from:', styled(os.getcwd(), fg_intense=True, bold=True)) print() - from ..tui.path_completer import PathCompleter + from ..tui.path_completer import get_path try: - dest = PathCompleter().input() + dest = get_path() except (KeyboardInterrupt, EOFError): return if dest: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kittens/tui/handler.py new/kitty-0.26.2/kittens/tui/handler.py --- old/kitty-0.26.1/kittens/tui/handler.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kittens/tui/handler.py 2022-09-05 07:19:50.000000000 +0200 @@ -121,11 +121,22 @@ self._tui_loop.quit(1) def on_key_event(self, key_event: KeyEventType, in_bracketed_paste: bool = False) -> None: + ' Override this method and perform_default_key_action() to handle all key events ' if key_event.text: self.on_text(key_event.text, in_bracketed_paste) else: self.on_key(key_event) + def perform_default_key_action(self, key_event: KeyEventType) -> bool: + ' Override in sub-class if you want to handle these key events yourself ' + if key_event.matches('ctrl+c'): + self.on_interrupt() + return True + if key_event.matches('ctrl+d'): + self.on_eot() + return True + return False + def on_text(self, text: str, in_bracketed_paste: bool = False) -> None: pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kittens/tui/loop.py new/kitty-0.26.2/kittens/tui/loop.py --- old/kitty-0.26.1/kittens/tui/loop.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kittens/tui/loop.py 2022-09-05 07:19:50.000000000 +0200 @@ -330,13 +330,8 @@ except Exception: pass else: - if k.matches('ctrl+c'): - self.handler.on_interrupt() - return - if k.matches('ctrl+d'): - self.handler.on_eot() - return - self.handler.on_key_event(k) + if not self.handler.perform_default_key_action(k): + self.handler.on_key_event(k) def _on_pm(self, pm: str) -> None: pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kittens/tui/path_completer.py new/kitty-0.26.2/kittens/tui/path_completer.py --- old/kitty-0.26.1/kittens/tui/path_completer.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kittens/tui/path_completer.py 2022-09-05 07:19:50.000000000 +0200 @@ -147,5 +147,9 @@ return '' +def get_path(prompt: str = '> ') -> str: + return PathCompleter(prompt).input() + + def develop() -> None: PathCompleter().input() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/boss.py new/kitty-0.26.2/kitty/boss.py --- old/kitty-0.26.1/kitty/boss.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/boss.py 2022-09-05 07:19:50.000000000 +0200 @@ -1702,7 +1702,10 @@ if actions: self.drain_actions(actions) if not found_action: - open_url(url, program or get_options().open_url_with, cwd=cwd) + extra_env = {} + if self.listening_on: + extra_env['KITTY_LISTEN_ON'] = self.listening_on + open_url(url, program or get_options().open_url_with, cwd=cwd, extra_env=extra_env) @ac('misc', 'Click a URL using the keyboard') def open_url_with_hints(self) -> None: @@ -2414,8 +2417,11 @@ @ac('debug', 'Show the environment variables that the kitty process sees') def show_kitty_env_vars(self) -> None: w = self.active_window + env = os.environ.copy() + if is_macos and env.get('LC_CTYPE') == 'UTF-8' and not getattr(sys, 'kitty_run_data').get('lc_ctype_before_python'): + del env['LC_CTYPE'] if w: - output = '\n'.join(f'{k}={v}' for k, v in os.environ.items()) + output = '\n'.join(f'{k}={v}' for k, v in env.items()) self.display_scrollback(w, output, title=_('Current kitty env vars'), report_cursor=False) @ac('debug', ''' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/child-monitor.c new/kitty-0.26.2/kitty/child-monitor.c --- old/kitty-0.26.1/kitty/child-monitor.c 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/child-monitor.c 2022-09-05 07:19:50.000000000 +0200 @@ -974,7 +974,7 @@ static void close_os_window(ChildMonitor *self, OSWindow *os_window) { - int w = os_window->window_width, h = os_window->window_height; + int w = os_window->content_area_width, h = os_window->content_area_height; if (os_window->before_fullscreen.is_set && is_os_window_fullscreen(os_window)) { w = os_window->before_fullscreen.w; h = os_window->before_fullscreen.h; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/child.c new/kitty-0.26.2/kitty/child.c --- old/kitty-0.26.1/kitty/child.c 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/child.c 2022-09-05 07:19:50.000000000 +0200 @@ -181,8 +181,26 @@ return PyLong_FromLong(pid); } +#ifdef __APPLE__ +#include <crt_externs.h> +#else +extern char **environ; +#endif + +static PyObject* +clearenv_py(PyObject *self UNUSED, PyObject *args UNUSED) { +#ifdef __APPLE__ + char **e = *_NSGetEnviron(); + if (e) *e = NULL; +#else + if (environ) *environ = NULL; +#endif + Py_RETURN_NONE; +} + static PyMethodDef module_methods[] = { METHODB(spawn, METH_VARARGS), + {"clearenv", clearenv_py, METH_NOARGS, ""}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/child.py new/kitty-0.26.2/kitty/child.py --- old/kitty-0.26.1/kitty/child.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/child.py 2022-09-05 07:19:50.000000000 +0200 @@ -169,7 +169,7 @@ def set_LANG_in_default_env(val: str) -> None: - default_env()['LANG'] = val + default_env().setdefault('LANG', val) def openpty() -> Tuple[int, int]: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/cli.py new/kitty-0.26.2/kitty/cli.py --- old/kitty-0.26.1/kitty/cli.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/cli.py 2022-09-05 07:19:50.000000000 +0200 @@ -187,6 +187,7 @@ def ref_hyperlink(x: str, prefix: str = '') -> str: t, q = text_and_target(x) url = f'kitty+doc://{hostname()}/#ref={prefix}{q}' + t = re.sub(r':([a-z]+):`([^`]+)`', r'\2', t) return hyperlink_for_url(url, t) @@ -293,7 +294,7 @@ else: prev_indent = 0 if prev_line: - current_cmd['help'] += '\n\n' + current_cmd['help'] += '\n' if current_cmd['help'].endswith('::') else '\n\n' else: state = NORMAL (seq if current_cmd.get('condition', True) else disabled).append(current_cmd) @@ -429,6 +430,8 @@ a('{}: {} {}{}'.format(title('Usage'), bold(yellow(appname)), optstring, usage)) a('') message = message or default_msg + # replace rst literal code block syntax + message = message.replace('::\n\n', ':\n\n') wa(prettify(message)) a('') if seq: @@ -448,6 +451,8 @@ blocks[-1] += dt if opt.get('help'): t = help_text.replace('%default', str(defval)).strip() + # replace rst literal code block syntax + t = t.replace('::\n\n', ':\n\n') t = t.replace('#placeholder_for_formatting#', '') wa(prettify(t), indent=4) if opt.get('choices'): @@ -801,6 +806,7 @@ Replay previously dumped commands. Specify the path to a dump file previously created by :option:`{appname} --dump-commands`. You can open a new kitty window to replay the commands with:: + {appname} sh -c "{appname} --replay-commands /path/to/dump/file; read" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/constants.py new/kitty-0.26.2/kitty/constants.py --- old/kitty-0.26.1/kitty/constants.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/constants.py 2022-09-05 07:19:50.000000000 +0200 @@ -22,7 +22,7 @@ appname: str = 'kitty' kitty_face = '????' -version: Version = Version(0, 26, 1) +version: Version = Version(0, 26, 2) str_version: str = '.'.join(map(str, version)) _plat = sys.platform.lower() is_macos: bool = 'darwin' in _plat diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/fast_data_types.pyi new/kitty-0.26.2/kitty/fast_data_types.pyi --- old/kitty-0.26.1/kitty/fast_data_types.pyi 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/fast_data_types.pyi 2022-09-05 07:19:50.000000000 +0200 @@ -1485,3 +1485,4 @@ def set_use_os_log(yes: bool) -> None: ... def get_docs_ref_map() -> bytes: ... +def clearenv() -> None: ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/glfw.c new/kitty-0.26.2/kitty/glfw.c --- old/kitty-0.26.1/kitty/glfw.c 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/glfw.c 2022-09-05 07:19:50.000000000 +0200 @@ -97,6 +97,22 @@ } +static void +adjust_window_size_for_csd(OSWindow *w, int width, int height, int *adjusted_width, int *adjusted_height) { + *adjusted_width = width; *adjusted_height = height; + if (global_state.is_wayland) { + int left = -1, top, right, bottom; + glfwGetWindowFrameSize(w->handle, &left, &top, &right, &bottom); + if (left > -1) { + *adjusted_width -= left + right; + *adjusted_height -= top + bottom; + *adjusted_width = MAX(0, *adjusted_width); + *adjusted_height = MAX(0, *adjusted_height); + } + } +} + + void update_os_window_viewport(OSWindow *window, bool notify_boss) { int w, h, fw, fh; @@ -135,6 +151,7 @@ window->viewport_height = MAX(window->viewport_height, min_height); window->window_width = MAX(w, min_width); window->window_height = MAX(h, min_height); + adjust_window_size_for_csd(window, window->window_width, window->window_height, &window->content_area_width, &window->content_area_height); if (notify_boss) { call_boss(on_window_resize, "KiiO", window->id, window->viewport_width, window->viewport_height, dpi_changed ? Py_True : Py_False); } @@ -652,13 +669,14 @@ static bool do_toggle_fullscreen(OSWindow *w, unsigned int flags, bool restore_sizes) { - int width, height, x, y; + int width, height, x, y, content_area_width, content_area_height; glfwGetWindowSize(w->handle, &width, &height); glfwGetWindowPos(w->handle, &x, &y); + adjust_window_size_for_csd(w, width, height, &content_area_width, &content_area_height); bool was_maximized = glfwGetWindowAttrib(w->handle, GLFW_MAXIMIZED); if (glfwToggleFullscreen(w->handle, flags)) { w->before_fullscreen.is_set = true; - w->before_fullscreen.w = width; w->before_fullscreen.h = height; w->before_fullscreen.x = x; w->before_fullscreen.y = y; + w->before_fullscreen.w = content_area_width; w->before_fullscreen.h = content_area_height; w->before_fullscreen.x = x; w->before_fullscreen.y = y; w->before_fullscreen.was_maximized = was_maximized; return true; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/keys.c new/kitty-0.26.2/kitty/keys.c --- old/kitty-0.26.1/kitty/keys.c 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/keys.c 2022-09-05 07:19:50.000000000 +0200 @@ -284,9 +284,9 @@ }; // SingleKey {{{ -typedef uint32_t keybitfield; -#define KEY_BITS 21 -#define MOD_BITS 10 +typedef uint64_t keybitfield; +#define KEY_BITS 51 +#define MOD_BITS 12 #if 1 << (MOD_BITS-1) < GLFW_MOD_KITTY #error "Not enough mod bits" #endif @@ -309,9 +309,9 @@ } SingleKey; static inline void -SingleKey_set_vals(SingleKey *self, long key, unsigned short mods, int is_native) { - if (key >= 0 && (unsigned long)key <= BIT_MASK(keybitfield, KEY_BITS)) { - keybitfield k = (keybitfield)(unsigned long)key; +SingleKey_set_vals(SingleKey *self, long long key, unsigned short mods, int is_native) { + if (key >= 0 && (unsigned long long)key <= BIT_MASK(keybitfield, KEY_BITS)) { + keybitfield k = (keybitfield)(unsigned long long)key; self->key.key = k & BIT_MASK(keybitfield, KEY_BITS); } if (!(mods & 1 << (MOD_BITS + 1))) self->key.mods = mods & BIT_MASK(u_int32_t, MOD_BITS); @@ -320,8 +320,8 @@ static PyObject * SingleKey_new(PyTypeObject *type, PyObject *args, PyObject *kw) { - long key = -1; unsigned short mods = 1 << (MOD_BITS + 1); int is_native = -1; - if (!PyArg_ParseTupleAndKeywords(args, kw, "|Hpl", SingleKey_kwds, &mods, &is_native, &key)) return NULL; + long long key = -1; unsigned short mods = 1 << (MOD_BITS + 1); int is_native = -1; + if (!PyArg_ParseTupleAndKeywords(args, kw, "|HpL", SingleKey_kwds, &mods, &is_native, &key)) return NULL; SingleKey *self = (SingleKey *)type->tp_alloc(type, 0); if (self) SingleKey_set_vals(self, key, mods, is_native); return (PyObject*)self; @@ -341,8 +341,8 @@ unsigned int mods = self->key.mods; if (mods) pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, "mods=%u, ", mods); if (self->key.is_native) pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, "is_native=True, "); - unsigned long key = self->key.key; - if (key) pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, "key=%lu, ", key); + unsigned long long key = self->key.key; + if (key) pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, "key=%llu, ", key); if (buf[pos-1] == ' ') pos -= 2; pos += PyOS_snprintf(buf + pos, sizeof(buf) - pos, ")"); return PyUnicode_FromString(buf); @@ -350,8 +350,8 @@ static PyObject* SingleKey_get_key(SingleKey *self, void UNUSED *closure) { - const unsigned long val = self->key.key; - return PyLong_FromUnsignedLong(val); + const unsigned long long val = self->key.key; + return PyLong_FromUnsignedLongLong(val); } static PyObject* @@ -436,8 +436,8 @@ static PyObject* SingleKey_replace(SingleKey *self, PyObject *args, PyObject *kw) { - long key = -2; unsigned short mods = 1 << (MOD_BITS + 1); int is_native = -1; - if (!PyArg_ParseTupleAndKeywords(args, kw, "|Hpl", SingleKey_kwds, &mods, &is_native, &key)) return NULL; + long long key = -2; unsigned short mods = 1 << (MOD_BITS + 1); int is_native = -1; + if (!PyArg_ParseTupleAndKeywords(args, kw, "|HpL", SingleKey_kwds, &mods, &is_native, &key)) return NULL; SingleKey *ans = (SingleKey*)SingleKey_Type.tp_alloc(&SingleKey_Type, 0); if (ans) { if (key == -1) key = 0; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/launch.py new/kitty-0.26.2/kitty/launch.py --- old/kitty-0.26.1/kitty/launch.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/launch.py 2022-09-05 07:19:50.000000000 +0200 @@ -21,7 +21,7 @@ ) from .options.utils import env as parse_env from .tabs import Tab, TabManager -from .types import run_once +from .types import OverlayType, run_once from .utils import ( get_editor, log_error, resolve_custom_file, set_primary_selection, which ) @@ -56,7 +56,7 @@ --type type=choices default=window -choices=window,tab,os-window,overlay,background,clipboard,primary +choices=window,tab,os-window,overlay,overlay-main,background,clipboard,primary Where to launch the child process: :code:`window` @@ -71,6 +71,13 @@ :code:`overlay` An :term:`overlay window <overlay>` covering the current active kitty window +:code:`overlay-main` + An :term:`overlay window <overlay>` covering the current active kitty window. + Unlike a plain overlay window, this window is considered as a :italic:`main` + window which means it is used as the active window for getting the current working + directory, the input text for kittens, launch commands, etc. Useful if this overlay is + intended to run for a long time as a primary window. + :code:`background` The process will be run in the :italic:`background`, without a kitty window. @@ -165,12 +172,14 @@ Restrict the actions remote control is allowed to take. This works like :opt:`remote_control_password`. You can specify a password and list of actions just as for :opt:`remote_control_password`. For example:: + --remote-control-password '"my passphrase" get-* set-colors' This password will be in effect for this window only. Note that any passwords you have defined for :opt:`remote_control_password` in :file:`kitty.conf` are also in effect. You can override them by using the same password here. You can also disable all :opt:`remote_control_password` global passwords for this window, by using:: + --remote-control-password '!' This option only takes effect if :option:`--allow-remote-control` @@ -271,8 +280,17 @@ type=list Change colors in the newly launched window. You can either specify a path to a :file:`.conf` file with the same syntax as :file:`kitty.conf` to read the colors -from, or specify them individually, for example: :code:`--color background=white ---color foreground=red`. +from, or specify them individually, for example:: + + --color background=white --color foreground=red + + +--spacing +type=list +Set the margin and padding for the newly created window. +For example: :code:`margin=20` or :code:`padding-left=10` or :code:`margin-h=30`. The shorthand form sets +all values, the :code:`*-h` and :code:`*-v` variants set horizontal and vertical values. +Can be specified multiple times. --watcher -w @@ -454,6 +472,10 @@ 'overlay_for': None, 'stdin': None } + spacing = {} + if opts.spacing: + from .rc.set_spacing import parse_spacing_settings, patch_window_edges + spacing = parse_spacing_settings(opts.spacing) if opts.cwd: if opts.cwd == 'current': if active: @@ -524,7 +546,7 @@ kw['cmd'] = final_cmd if force_window_launch and opts.type not in non_window_launch_types: opts.type = 'window' - if opts.type == 'overlay' and active: + if opts.type in ('overlay', 'overlay-main') and active: kw['overlay_for'] = active.id if opts.type == 'background': cmd = kw['cmd'] @@ -549,12 +571,17 @@ if tab is not None: watchers = load_watch_modules(opts.watcher) new_window: Window = tab.new_window(env=env or None, watchers=watchers or None, is_clone_launch=is_clone_launch, **kw) + if spacing: + patch_window_edges(new_window, spacing) + tab.relayout() if opts.color: apply_colors(new_window, opts.color) if opts.keep_focus and active: boss.set_active_window(active, switch_os_window_if_needed=True, for_keep_focus=True) if opts.logo: new_window.set_logo(opts.logo, opts.logo_position or '', opts.logo_alpha) + if opts.type == 'overlay-main': + new_window.overlay_type = OverlayType.main return new_window return None @@ -564,7 +591,7 @@ return frozenset(( 'window_title', 'tab_title', 'type', 'keep_focus', 'cwd', 'env', 'hold', 'location', 'os_window_class', 'os_window_name', 'os_window_title', - 'logo', 'logo_position', 'logo_alpha', 'color' + 'logo', 'logo_position', 'logo_alpha', 'color', 'spacing', )) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/launcher/main.c new/kitty-0.26.2/kitty/launcher/main.c --- old/kitty-0.26.1/kitty/launcher/main.c 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/launcher/main.c 2022-09-05 07:19:50.000000000 +0200 @@ -17,6 +17,7 @@ #include <stdint.h> #include <wchar.h> #include <Python.h> +#include <fcntl.h> #ifndef KITTY_LIB_PATH #define KITTY_LIB_PATH "../.." @@ -268,9 +269,55 @@ } #endif // }}} +static bool +is_valid_fd(int fd) +{ + // This is copied from the python source code as we need the exact same semantics + // to prevent python from giving us None for sys.stdout and friends. +#if defined(F_GETFD) && ( \ + defined(__linux__) || \ + defined(__APPLE__) || \ + defined(__wasm__)) + return fcntl(fd, F_GETFD) >= 0; +#elif defined(__linux__) + int fd2 = dup(fd); + if (fd2 >= 0) { + close(fd2); + } + return (fd2 >= 0); +#else + struct stat st; + return (fstat(fd, &st) == 0); +#endif +} + +static bool +reopen_to_null(const char *mode, FILE *stream) { + errno = 0; + while (true) { + if (freopen("/dev/null", mode, stream) != NULL) return true; + if (errno == EINTR) continue; + perror("Failed to re-open STDIO handle to /dev/null"); + return false; + } +} + +static bool +ensure_working_stdio(void) { +#define C(which, mode) { \ + int fd = fileno(which); \ + if (fd < 0) { if (!reopen_to_null(mode, which)) return false; } \ + else if (!is_valid_fd(fd)) { \ + close(fd); if (!reopen_to_null(mode, which)) return false; \ + }} + C(stdin, "r") C(stdout, "w") C(stderr, "w") + return true; +#undef C +} int main(int argc, char *argv[], char* envp[]) { if (argc < 1 || !argv) { fprintf(stderr, "Invalid argc/argv\n"); return 1; } + if (!ensure_working_stdio()) return 1; char exe[PATH_MAX+1] = {0}; char exe_dir_buf[PATH_MAX+1] = {0}; FREE_AFTER_FUNCTION const char *lc_ctype = NULL; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/main.py new/kitty-0.26.2/kitty/main.py --- old/kitty-0.26.1/kitty/main.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/main.py 2022-09-05 07:19:50.000000000 +0200 @@ -379,6 +379,7 @@ 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: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/notify.py new/kitty-0.26.2/kitty/notify.py --- old/kitty-0.26.1/kitty/notify.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/notify.py 2022-09-05 07:19:50.000000000 +0200 @@ -1,13 +1,15 @@ #!/usr/bin/env python3 # License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net> +import re from base64 import standard_b64decode from collections import OrderedDict from itertools import count -from typing import Dict, Optional, Callable +from typing import Callable, Dict, Optional from .constants import is_macos, logo_png_file from .fast_data_types import get_boss +from .types import run_once from .utils import log_error NotifyImplementation = Callable[[str, str, str], None] @@ -92,6 +94,11 @@ return ans +@run_once +def sanitize_identifier_pat() -> 're.Pattern[str]': + return re.compile(r'[^a-zA-Z0-9-_+.]+') + + def parse_osc_99(raw: str) -> NotificationCommand: cmd = NotificationCommand() metadata, payload = raw.partition(';')[::2] @@ -107,7 +114,7 @@ if k == 'p': payload_type = v elif k == 'i': - cmd.identifier = v + cmd.identifier = sanitize_identifier_pat().sub('', v) elif k == 'e': payload_is_encoded = v == '1' elif k == 'd': diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/prewarm.py new/kitty-0.26.2/kitty/prewarm.py --- old/kitty-0.26.1/kitty/prewarm.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/prewarm.py 2022-09-05 07:19:50.000000000 +0200 @@ -24,9 +24,9 @@ from kitty.constants import kitty_exe, running_in_kitty from kitty.entry_points import main as main_entry_point from kitty.fast_data_types import ( - CLD_EXITED, CLD_KILLED, CLD_STOPPED, get_options, install_signal_handlers, - read_signals, remove_signal_handlers, safe_pipe, set_options, - set_use_os_log + CLD_EXITED, CLD_KILLED, CLD_STOPPED, clearenv, get_options, + install_signal_handlers, read_signals, remove_signal_handlers, safe_pipe, + set_options, set_use_os_log ) from kitty.options.types import Options from kitty.shm import SharedMemory @@ -293,6 +293,13 @@ env = cmd.get('env') if env is not None: os.environ.clear() + # os.environ.clear() does not delete all existing env vars from the + # libc environ pointer in some circumstances, I havent figured out + # which exactly. Presumably there is something that alters the + # libc environ pointer?? The environ pointer is used by os.exec and + # therefore by subprocess and friends, so we need to ensure it is + # cleared. + clearenv() os.environ.update(env) argv = cmd.get('argv') if argv: @@ -567,8 +574,9 @@ os.set_inheritable(stdout_write, False) os.set_inheritable(death_notify_write, False) running_in_kitty(False) - if not sys.stdout.line_buffering: # happens if the parent kitty instance has stdout not pointing to a terminal - sys.stdout.reconfigure(line_buffering=True) # type: ignore + for x in (sys.stdout, sys.stdin, sys.stderr): + if not x.line_buffering: # happens if the parent kitty instance has stdout not pointing to a terminal + x.reconfigure(line_buffering=True) # type: ignore try: main(stdin_read, stdout_write, death_notify_write) finally: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/rc/launch.py new/kitty-0.26.2/kitty/rc/launch.py --- old/kitty-0.26.1/kitty/rc/launch.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/rc/launch.py 2022-09-05 07:19:50.000000000 +0200 @@ -41,6 +41,7 @@ @first_cmd_output_on_screen.@last_cmd_output.@last_visited_cmd_output: Where to get stdin for the process from stdin_add_formatting/bool: Boolean indicating whether to add formatting codes to stdin stdin_add_line_wrap_markers/bool: Boolean indicating whether to add line wrap markers to stdin + spacing/list.str: A list of spacing specifications, see the docs for the set-spacing command no_response/bool: Boolean indicating whether to send back the window id marker/str: Specification for marker for new window, for example: "text 1 ERROR" logo/str: Path to window logo @@ -53,8 +54,8 @@ desc = ( 'Prints out the id of the newly opened window. Any command line arguments' ' are assumed to be the command line used to run in the new window, if none' - ' are provided, the default shell is run. For example:\n' - ':code:`kitty @ launch --title=Email mutt`' + ' are provided, the default shell is run. For example::\n\n' + ' kitty @ launch --title=Email mutt' ) options_spec = MATCH_TAB_OPTION + '\n\n' + '''\ --no-response diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/rc/new_window.py new/kitty-0.26.2/kitty/rc/new_window.py --- old/kitty-0.26.1/kitty/rc/new_window.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/rc/new_window.py 2022-09-05 07:19:50.000000000 +0200 @@ -34,8 +34,8 @@ ' Prints out the id of the newly opened window' ' (unless :option:`--no-response` is used). Any command line arguments' ' are assumed to be the command line used to run in the new window, if none' - ' are provided, the default shell is run. For example:\n' - ':code:`kitty @ new-window --title Email mutt`' + ' are provided, the default shell is run. For example::\n\n' + ' kitty @ new-window --title Email mutt' ) options_spec = MATCH_TAB_OPTION + '''\n --title diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/rc/set_background_opacity.py new/kitty-0.26.2/kitty/rc/set_background_opacity.py --- old/kitty-0.26.1/kitty/rc/set_background_opacity.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/rc/set_background_opacity.py 2022-09-05 07:19:50.000000000 +0200 @@ -27,7 +27,8 @@ desc = ( 'Set the background opacity for the specified windows. This will only work if you have turned on' ' :opt:`dynamic_background_opacity` in :file:`kitty.conf`. The background opacity affects all kitty windows in a' - ' single OS window. For example: :code:`kitty @ set-background-opacity 0.5`' + ' single OS window. For example::\n\n' + ' kitty @ set-background-opacity 0.5' ) options_spec = '''\ --all -a diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/rc/set_colors.py new/kitty-0.26.2/kitty/rc/set_colors.py --- old/kitty-0.26.1/kitty/rc/set_colors.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/rc/set_colors.py 2022-09-05 07:19:50.000000000 +0200 @@ -68,7 +68,8 @@ 'Set the terminal colors for the specified windows/tabs (defaults to active window).' ' You can either specify the path to a conf file' ' (in the same format as :file:`kitty.conf`) to read the colors from or you can specify individual colors,' - ' for example: :code:`kitty @ set-colors foreground=red background=white`' + ' for example::\n\n' + ' kitty @ set-colors foreground=red background=white' ) options_spec = '''\ --all -a diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/rc/set_spacing.py new/kitty-0.26.2/kitty/rc/set_spacing.py --- old/kitty-0.26.1/kitty/rc/set_spacing.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/rc/set_spacing.py 2022-09-05 07:19:50.000000000 +0200 @@ -2,7 +2,7 @@ # License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net> -from typing import TYPE_CHECKING, Dict, Optional, List +from typing import TYPE_CHECKING, Dict, Iterable, List, Optional from .base import ( MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, PayloadGetType, @@ -37,6 +37,34 @@ setattr(opts, q, new_edges) +def parse_spacing_settings(args: Iterable[str]) -> Dict[str, Optional[float]]: + mapper: Dict[str, List[str]] = {} + for q in ('margin', 'padding'): + mapper[q] = f'{q}-left {q}-top {q}-right {q}-bottom'.split() + mapper[f'{q}-h'] = mapper[f'{q}-horizontal'] = f'{q}-left {q}-right'.split() + mapper[f'{q}-v'] = mapper[f'{q}-vertical'] = f'{q}-top {q}-bottom'.split() + for edge in ('left', 'top', 'right', 'bottom'): + mapper[f'{q}-{edge}'] = [f'{q}-{edge}'] + settings: Dict[str, Optional[float]] = {} + for spec in args: + parts = spec.split('=', 1) + if len(parts) != 2: + raise ValueError(f'{spec} is not a valid setting') + which = mapper.get(parts[0].lower()) + if not which: + raise ValueError(f'{parts[0]} is not a valid edge specification') + if parts[1].lower() == 'default': + val = None + else: + try: + val = float(parts[1]) + except Exception: + raise ValueError(f'{parts[1]} is not a number') + for q in which: + settings[q] = val + return settings + + class SetSpacing(RemoteCommand): ''' @@ -70,32 +98,12 @@ argspec = 'MARGIN_OR_PADDING ...' def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: - settings: Dict[str, Optional[float]] = {} - mapper: Dict[str, List[str]] = {} - for q in ('margin', 'padding'): - mapper[q] = f'{q}-left {q}-top {q}-right {q}-bottom'.split() - mapper[f'{q}-h'] = mapper[f'{q}-horizontal'] = f'{q}-left {q}-right'.split() - mapper[f'{q}-v'] = mapper[f'{q}-vertical'] = f'{q}-top {q}-bottom'.split() - for edge in ('left', 'top', 'right', 'bottom'): - mapper[f'{q}-{edge}'] = [f'{q}-{edge}'] if not args: self.fatal('At least one setting must be specified') - for spec in args: - parts = spec.split('=', 1) - if len(parts) != 2: - self.fatal(f'{spec} is not a valid setting') - which = mapper.get(parts[0].lower()) - if not which: - self.fatal(f'{parts[0]} is not a valid edge specification') - if parts[1].lower() == 'default': - val = None - else: - try: - val = float(parts[1]) - except Exception: - self.fatal(f'{parts[1]} is not a number') - for q in which: - settings[q] = val + try: + settings = parse_spacing_settings(args) + except Exception as e: + self.fatal(str(e)) ans = { 'match_window': opts.match, 'match_tab': opts.match_tab, 'all': opts.all, 'configured': opts.configured, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/shell.py new/kitty-0.26.2/kitty/shell.py --- old/kitty-0.26.1/kitty/shell.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/shell.py 2022-09-05 07:19:50.000000000 +0200 @@ -2,7 +2,6 @@ # License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net> import os -import readline import shlex import sys import traceback @@ -39,6 +38,7 @@ @run_once def init_readline() -> None: + import readline global is_libedit with suppress(OSError): readline.read_init_file() @@ -90,6 +90,7 @@ self.history_path = os.path.join(ddir, 'shell.history') def complete(self, text: str, state: int) -> Optional[str]: + import readline if state == 0: line = readline.get_line_buffer() cmdline = shlex.split(line) @@ -102,6 +103,7 @@ return None def __enter__(self) -> 'Completer': + import readline with suppress(Exception): readline.read_history_file(self.history_path) readline.set_completer(self.complete) @@ -110,6 +112,7 @@ return self def __exit__(self, *a: Any) -> None: + import readline readline.write_history_file(self.history_path) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/state.h new/kitty-0.26.2/kitty/state.h --- old/kitty-0.26.1/kitty/state.h 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/state.h 2022-09-05 07:19:50.000000000 +0200 @@ -188,7 +188,7 @@ int x, y, w, h; bool is_set, was_maximized; } before_fullscreen; - int viewport_width, viewport_height, window_width, window_height; + int viewport_width, viewport_height, window_width, window_height, content_area_width, content_area_height; double viewport_x_ratio, viewport_y_ratio; Tab *tabs; BackgroundImage *bgimage; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/tabs.py new/kitty-0.26.2/kitty/tabs.py --- old/kitty-0.26.1/kitty/tabs.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/tabs.py 2022-09-05 07:19:50.000000000 +0200 @@ -211,7 +211,7 @@ @property def active_window_for_cwd(self) -> Optional[Window]: - return self.windows.active_group_base + return self.windows.active_group_main @property def title(self) -> str: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/types.py new/kitty-0.26.2/kitty/types.py --- old/kitty-0.26.1/kitty/types.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/types.py 2022-09-05 07:19:50.000000000 +0200 @@ -1,16 +1,24 @@ #!/usr/bin/env python # License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net> +from enum import Enum from functools import update_wrapper from typing import ( - TYPE_CHECKING, Any, Callable, Generic, NamedTuple, Tuple, TypeVar, Union, Iterator, Dict + TYPE_CHECKING, Any, Callable, Dict, Generic, Iterator, NamedTuple, Tuple, + TypeVar, Union ) + if TYPE_CHECKING: from kitty.fast_data_types import SingleKey _T = TypeVar('_T') +class OverlayType(Enum): + transient: str = 'transient' + main: str = 'main' + + class ParsedShortcut(NamedTuple): mods: int key_name: str @@ -160,11 +168,11 @@ @run_once def modmap() -> Dict[str, int]: + from .constants import is_macos from .fast_data_types import ( GLFW_MOD_ALT, GLFW_MOD_CAPS_LOCK, GLFW_MOD_CONTROL, GLFW_MOD_HYPER, GLFW_MOD_META, GLFW_MOD_NUM_LOCK, GLFW_MOD_SHIFT, GLFW_MOD_SUPER ) - from .constants import is_macos return {'ctrl': GLFW_MOD_CONTROL, 'shift': GLFW_MOD_SHIFT, ('opt' if is_macos else 'alt'): GLFW_MOD_ALT, ('cmd' if is_macos else 'super'): GLFW_MOD_SUPER, 'hyper': GLFW_MOD_HYPER, 'meta': GLFW_MOD_META, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/utils.py new/kitty-0.26.2/kitty/utils.py --- old/kitty-0.26.1/kitty/utils.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/utils.py 2022-09-05 07:19:50.000000000 +0200 @@ -269,7 +269,8 @@ return cmd -def open_cmd(cmd: Union[Iterable[str], List[str]], arg: Union[None, Iterable[str], str] = None, cwd: Optional[str] = None) -> 'PopenType[bytes]': +def open_cmd(cmd: Union[Iterable[str], List[str]], arg: Union[None, Iterable[str], str] = None, + cwd: Optional[str] = None, extra_env: Optional[Dict[str, str]] = None) -> 'PopenType[bytes]': import subprocess if arg is not None: cmd = list(cmd) @@ -277,13 +278,17 @@ cmd.append(arg) else: cmd.extend(arg) + env: Optional[Dict[str, str]] = None + if extra_env: + env = os.environ.copy() + env.update(extra_env) return subprocess.Popen( tuple(cmd), stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, cwd=cwd or None, - preexec_fn=clear_handled_signals) + preexec_fn=clear_handled_signals, env=env) -def open_url(url: str, program: Union[str, List[str]] = 'default', cwd: Optional[str] = None) -> 'PopenType[bytes]': - return open_cmd(command_for_open(program), url, cwd=cwd) +def open_url(url: str, program: Union[str, List[str]] = 'default', cwd: Optional[str] = None, extra_env: Optional[Dict[str, str]] = None) -> 'PopenType[bytes]': + return open_cmd(command_for_open(program), url, cwd=cwd, extra_env=extra_env) def detach(fork: bool = True, setsid: bool = True, redirect: bool = True) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/window.py new/kitty-0.26.2/kitty/window.py --- old/kitty-0.26.1/kitty/window.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/window.py 2022-09-05 07:19:50.000000000 +0200 @@ -41,11 +41,11 @@ update_window_title, update_window_visibility, wakeup_main_loop ) from .keys import keyboard_mode_name, mod_mask -from .notify import NotificationCommand, handle_notification_cmd +from .notify import NotificationCommand, handle_notification_cmd, sanitize_identifier_pat from .options.types import Options from .rgb import to_color from .terminfo import get_capabilities -from .types import MouseEvent, WindowGeometry, ac, run_once +from .types import MouseEvent, OverlayType, WindowGeometry, ac, run_once from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict from .utils import ( docs_url, get_primary_selection, kitty_ansi_sanitizer_pat, load_shaders, @@ -462,6 +462,7 @@ class Window: window_custom_type: str = '' + overlay_type = OverlayType.transient def __init__( self, @@ -642,6 +643,8 @@ } if self.window_custom_type: ans['window_custom_type'] = self.window_custom_type + if self.overlay_type is not OverlayType.transient: + ans['overlay_type'] = self.overlay_type.value return ans @property @@ -998,6 +1001,7 @@ self.screen.send_escape_code_to_child(OSC, f'{code};rgb:{r:04x}/{g:04x}/{b:04x}') def report_notification_activated(self, identifier: str) -> None: + identifier = sanitize_identifier_pat().sub('', identifier) self.screen.send_escape_code_to_child(OSC, f'99;i={identifier};') def set_dynamic_color(self, code: int, value: Union[str, bytes]) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty/window_list.py new/kitty-0.26.2/kitty/window_list.py --- old/kitty-0.26.1/kitty/window_list.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty/window_list.py 2022-09-05 07:19:50.000000000 +0200 @@ -7,7 +7,7 @@ from itertools import count from typing import Any, Deque, Dict, Iterator, List, Optional, Tuple, Union -from .types import WindowGeometry +from .types import OverlayType, WindowGeometry from .typing import EdgeLiteral, TabType, WindowType WindowOrId = Union[WindowType, int] @@ -54,7 +54,10 @@ return False @property - def base_window_id(self) -> int: + def main_window_id(self) -> int: + for w in reversed(self.windows): + if w.overlay_type is OverlayType.main: + return w.id return self.windows[0].id if self.windows else 0 @property @@ -298,9 +301,9 @@ return None @property - def active_group_base(self) -> Optional[WindowType]: + def active_group_main(self) -> Optional[WindowType]: with suppress(Exception): - return self.id_map[self.groups[self.active_group_idx].base_window_id] + return self.id_map[self.groups[self.active_group_idx].main_window_id] return None def set_active_window_group_for(self, x: WindowOrId, for_keep_focus: Optional[WindowType] = None) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty_tests/check_build.py new/kitty-0.26.2/kitty_tests/check_build.py --- old/kitty-0.26.1/kitty_tests/check_build.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty_tests/check_build.py 2022-09-05 07:19:50.000000000 +0200 @@ -107,6 +107,22 @@ run_tests(partial(docs_url, local_docs_root=None), w, '/') self.ae(docs_url('#ref=issues-123'), 'https://github.com/kovidgoyal/kitty/issues/123') + def test_launcher_ensures_stdio(self): + from kitty.constants import kitty_exe + import subprocess + exe = kitty_exe() + cp = subprocess.run([exe, '+runpy', f'''\ +import os, sys +if sys.stdin: + os.close(sys.stdin.fileno()) +if sys.stdout: + os.close(sys.stdout.fileno()) +if sys.stderr: + os.close(sys.stderr.fileno()) +os.execlp({exe!r}, 'kitty', '+runpy', 'import sys; raise SystemExit(1 if sys.stdout is None or sys.stdin is None or sys.stderr is None else 0)') +''']) + self.assertEqual(cp.returncode, 0) + def main() -> None: tests = unittest.defaultTestLoader.loadTestsFromTestCase(TestBuild) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty_tests/datatypes.py new/kitty-0.26.2/kitty_tests/datatypes.py --- old/kitty-0.26.1/kitty_tests/datatypes.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty_tests/datatypes.py 2022-09-05 07:19:50.000000000 +0200 @@ -535,7 +535,7 @@ q('a\x1b[bc', 'ac') q('a\x1b[12;34:43mbc', 'abc') - def test_SingleKey(self): + def test_single_key(self): from kitty.fast_data_types import ( GLFW_MOD_KITTY, GLFW_MOD_SHIFT, SingleKey ) @@ -548,9 +548,14 @@ self.ae(repr(SingleKey(key=23, mods=2, is_native=True)), 'SingleKey(mods=2, is_native=True, key=23)') self.ae(repr(SingleKey(key=23, mods=2)), 'SingleKey(mods=2, key=23)') self.ae(repr(SingleKey(key=23)), 'SingleKey(key=23)') + self.ae(repr(SingleKey(key=0x1008ff57)), 'SingleKey(key=269025111)') self.ae(repr(SingleKey(key=23)._replace(mods=2)), 'SingleKey(mods=2, key=23)') self.ae(repr(SingleKey(key=23)._replace(key=-1, mods=GLFW_MOD_KITTY)), f'SingleKey(mods={GLFW_MOD_KITTY})') self.assertEqual(SingleKey(key=1), SingleKey(key=1)) self.assertEqual(hash(SingleKey(key=1)), hash(SingleKey(key=1))) self.assertNotEqual(hash(SingleKey(key=1, mods=2)), hash(SingleKey(key=1))) self.assertNotEqual(SingleKey(key=1, mods=2), SingleKey(key=1)) + + def test_notify_identifier_sanitization(self): + from kitty.notify import sanitize_identifier_pat + self.ae(sanitize_identifier_pat().sub('', '\x1b\nabc\n[*'), 'abc') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty_tests/hints.py new/kitty-0.26.2/kitty_tests/hints.py --- old/kitty-0.26.1/kitty_tests/hints.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty_tests/hints.py 2022-09-05 07:19:50.000000000 +0200 @@ -10,13 +10,14 @@ def test_url_hints(self): from kittens.hints.main import ( Mark, convert_text, functions_for, linenum_marks, - linenum_process_result, mark, parse_hints_args + linenum_process_result, mark, parse_hints_args, process_escape_codes ) args = parse_hints_args([])[0] pattern, post_processors = functions_for(args) def create_marks(text, cols=20, mark=mark): text = convert_text(text, cols) + text, _ = process_escape_codes(text) return tuple(mark(pattern, post_processors, text, args)) def t(text, url, cols=20): @@ -32,6 +33,8 @@ t(f'link:{u}[xxx]', u) t(f'`xyz <{u}>`_.', u) t(f'<a href="{u}">moo', u) + t('\x1b[mhttp://test.me/1234\n\x1b[mx', 'http://test.me/1234') + t('\x1b[mhttp://test.me/12345\r\x1b[m6\n\x1b[mx', 'http://test.me/123456') def m(text, path, line, cols=20): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty_tests/prewarm.py new/kitty-0.26.2/kitty_tests/prewarm.py --- old/kitty-0.26.1/kitty_tests/prewarm.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty_tests/prewarm.py 2022-09-05 07:19:50.000000000 +0200 @@ -28,13 +28,15 @@ cwd = tempfile.gettempdir() env = {'TEST_ENV_PASS': 'xyz'} - cols = 117 + cols = 317 stdin_data = 'from_stdin' pty = self.create_pty(cols=cols) ttyname = os.ttyname(pty.slave_fd) opts = get_options() opts.config_overrides = 'font_family prewarm', + os.environ['SHOULD_NOT_BE_PRESENT'] = '1' p = fork_prewarm_process(opts, use_exec=True) + del os.environ['SHOULD_NOT_BE_PRESENT'] if p is None: return p.take_from_worker_fd(create_file=True) @@ -44,7 +46,7 @@ 'ttyname': os.ttyname(sys.stdout.fileno()), 'cols': read_screen_size().cols, 'cwd': os.getcwd(), - 'env': os.environ.get('TEST_ENV_PASS'), + 'env': os.environ.copy(), 'pid': os.getpid(), 'font_family': get_options().font_family, 'stdin': sys.stdin.read(), @@ -59,7 +61,8 @@ self.assertTrue(data['cterm']) self.ae(data['ttyname'], ttyname) self.ae(os.path.realpath(data['cwd']), os.path.realpath(cwd)) - self.ae(data['env'], env['TEST_ENV_PASS']) + self.ae(data['env']['TEST_ENV_PASS'], env['TEST_ENV_PASS']) + self.assertNotIn('SHOULD_NOT_BE_PRESENT', data['env']) self.ae(data['font_family'], 'prewarm') self.ae(int(p.from_worker.readline()), data['pid']) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/kitty_tests/shell_integration.py new/kitty-0.26.2/kitty_tests/shell_integration.py --- old/kitty-0.26.1/kitty_tests/shell_integration.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/kitty_tests/shell_integration.py 2022-09-05 07:19:50.000000000 +0200 @@ -368,4 +368,4 @@ pty.send_cmd_to_child('clone-in-kitty') pty.wait_till(lambda: len(pty.callbacks.clone_cmds) == 1) env = pty.callbacks.clone_cmds[0].env - self.ae(env.get('ES'), 'a\n `b` c\n$d') + self.ae(env.get('ES'), 'a\n `b` c\n$d', f'Screen contents: {pty.screen_contents()!r}') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kitty-0.26.1/publish.py new/kitty-0.26.2/publish.py --- old/kitty-0.26.1/publish.py 2022-08-29 21:34:42.000000000 +0200 +++ new/kitty-0.26.2/publish.py 2022-09-05 07:19:50.000000000 +0200 @@ -269,13 +269,7 @@ def update_nightly_description(self, release_id: int) -> None: url = f'{self.url_base}/{release_id}' now = str(datetime.datetime.utcnow()).split('.')[0] + ' UTC' - try: - with open('.git/refs/heads/master') as f: - commit = f.read().strip() - except FileNotFoundError: - time.sleep(1) - with open('.git/refs/heads/master') as f: - commit = f.read().strip() + commit = subprocess.check_output(['git', 'rev-parse', '--verify', '--end-of-options', 'master^{commit}']).decode('utf-8').strip() self.patch( url, 'Failed to update nightly release description', body=f'Nightly release, generated on: {now} from commit: {commit}.' @@ -368,7 +362,7 @@ 'target_commitish': 'master', 'name': f'version {self.version}', 'body': f'Release version {self.version}.' - ' For changelog, see https://sw.kovidgoyal.net/kitty/changelog/' + ' For changelog, see https://sw.kovidgoyal.net/kitty/changelog/#detailed-list-of-changes' ' GPG key used for signing tarballs is: https://calibre-ebook.com/signatures/kovid.gpg', 'draft': False, 'prerelease': False