diff --git a/kitty/entry_points.py b/kitty/entry_points.py index bdae03d96..e706176e9 100644 --- a/kitty/entry_points.py +++ b/kitty/entry_points.py @@ -26,19 +26,9 @@ def runpy(args: List[str]) -> None: def hold(args: List[str]) -> None: - import subprocess - ret = 1 - try: - ret = subprocess.Popen(args[1:]).wait() - except KeyboardInterrupt: - pass - except FileNotFoundError: - print(f'Could not find {args[1]!r} to execute', file=sys.stderr) - except Exception as e: - print(e, file=sys.stderr) - from kitty.utils import hold_till_enter - hold_till_enter() - raise SystemExit(ret) + from kitty.constants import kitty_tool_exe + args = ['kitty-tool', '__hold_till_enter__'] + args[1:] + os.execvp(kitty_tool_exe(), args) def open_urls(args: List[str]) -> None: diff --git a/kitty/launch.py b/kitty/launch.py index 4dd3ae191..49cc650f7 100644 --- a/kitty/launch.py +++ b/kitty/launch.py @@ -15,7 +15,7 @@ from .child import Child from .cli import parse_args from .cli_stub import LaunchCLIOptions from .clipboard import set_clipboard_string, set_primary_selection -from .constants import kitty_exe, shell_path +from .constants import kitty_tool_exe, shell_path from .fast_data_types import ( add_timer, get_boss, get_options, get_os_window_title, patch_color_profiles ) @@ -565,7 +565,7 @@ def launch( else: if opts.hold: cmd = kw['cmd'] or [shell_path] - kw['cmd'] = [kitty_exe(), '+hold'] + cmd + kw['cmd'] = [kitty_tool_exe(), '__hold_till_enter__'] + cmd if force_target_tab: tab = target_tab else: diff --git a/kitty/session.py b/kitty/session.py index 62f2e5576..58948df40 100644 --- a/kitty/session.py +++ b/kitty/session.py @@ -8,7 +8,7 @@ from typing import ( ) from .cli_stub import CLIOptions -from .constants import kitty_exe +from .constants import kitty_tool_exe from .layout.interface import all_layouts from .options.types import Options from .options.utils import resize_window, to_layout_names, window_size @@ -211,7 +211,7 @@ def create_sessions( if special_window is None: cmd = args.args if args and args.args else resolved_shell(opts) if args and args.hold: - cmd = [kitty_exe(), '+hold'] + cmd + cmd = [kitty_tool_exe(), '__hold_till_enter__'] + cmd from kitty.tabs import SpecialWindow cwd: Optional[str] = args.directory if respect_cwd and args else None special_window = SpecialWindow(cmd, cwd_from=cwd_from, cwd=cwd) diff --git a/kitty/tabs.py b/kitty/tabs.py index 565c6c5ab..164f19bc3 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -17,7 +17,7 @@ from typing import ( from .borders import Border, Borders from .child import Child from .cli_stub import CLIOptions -from .constants import appname, kitty_exe +from .constants import appname, kitty_tool_exe from .fast_data_types import ( GLFW_MOUSE_BUTTON_LEFT, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, GLFW_RELEASE, add_tab, attach_window, current_focused_os_window_id, detach_window, get_boss, @@ -413,7 +413,7 @@ class Tab: # {{{ cmd[:0] = shlex.split(line) else: cmd[:0] = [resolved_shell(get_options())[0]] - cmd[:0] = [kitty_exe(), '+hold'] + cmd[:0] = [kitty_tool_exe(), '__hold_till_enter__'] fenv: Dict[str, str] = {} if env: fenv.update(env) diff --git a/tools/cmd/tool/main.go b/tools/cmd/tool/main.go index 248ba2c43..061cd9fb4 100644 --- a/tools/cmd/tool/main.go +++ b/tools/cmd/tool/main.go @@ -10,6 +10,7 @@ import ( "kitty/tools/cmd/clipboard" "kitty/tools/cmd/edit_in_kitty" "kitty/tools/cmd/update_self" + "kitty/tools/tui" ) var _ = fmt.Print @@ -25,4 +26,14 @@ func KittyToolEntryPoints(root *cli.Command) { edit_in_kitty.EntryPoint(root) // clipboard clipboard.EntryPoint(root) + // __hold_till_enter__ + root.AddSubCommand(&cli.Command{ + Name: "__hold_till_enter__", + Hidden: true, + OnlyArgsAllowed: true, + Run: func(cmd *cli.Command, args []string) (rc int, err error) { + tui.ExecAndHoldTillEnter(args) + return + }, + }) } diff --git a/tools/tui/hold.go b/tools/tui/hold.go new file mode 100644 index 000000000..2bf33d3ed --- /dev/null +++ b/tools/tui/hold.go @@ -0,0 +1,72 @@ +// License: GPLv3 Copyright: 2022, Kovid Goyal, + +package tui + +import ( + "errors" + "fmt" + "os" + "os/exec" + + "kitty/tools/tui/loop" +) + +var _ = fmt.Print + +func HoldTillEnter(start_with_newline bool) { + lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors, loop.NoMouseTracking) + if err != nil { + return + } + lp.OnInitialize = func() (string, error) { + lp.SetCursorVisible(false) + if start_with_newline { + lp.QueueWriteString("\r\n") + } + lp.QueueWriteString("\x1b[1;32mPress Enter or Esc to exit\x1b[m") + return "", nil + } + lp.OnFinalize = func() string { + lp.SetCursorVisible(true) + return "" + } + + lp.OnKeyEvent = func(event *loop.KeyEvent) error { + if event.MatchesPressOrRepeat("enter") || event.MatchesPressOrRepeat("esc") || event.MatchesPressOrRepeat("ctrl+c") || event.MatchesPressOrRepeat("ctrl+d") { + event.Handled = true + lp.Quit(0) + } + return nil + } + lp.Run() +} + +func ExecAndHoldTillEnter(cmdline []string) { + if len(cmdline) == 0 { + HoldTillEnter(false) + os.Exit(0) + } + var cmd *exec.Cmd + if len(cmdline) == 1 { + cmd = exec.Command(cmdline[0]) + } else { + cmd = exec.Command(cmdline[0], cmdline[1:]...) + } + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + var ee *exec.ExitError + err := cmd.Run() + is_exit_error := err != nil && errors.As(err, &ee) + if err != nil && !is_exit_error { + fmt.Fprintln(os.Stderr, err) + } + HoldTillEnter(true) + if err == nil { + os.Exit(0) + } + if is_exit_error { + os.Exit(ee.ExitCode()) + } + os.Exit(1) +} diff --git a/tools/tui/loop/api.go b/tools/tui/loop/api.go index b2d811fa2..401cca373 100644 --- a/tools/tui/loop/api.go +++ b/tools/tui/loop/api.go @@ -242,6 +242,14 @@ func (self *Loop) SetCursorShape(shape CursorShapes, blink bool) { self.QueueWriteString(CursorShape(shape, blink)) } +func (self *Loop) SetCursorVisible(visible bool) { + if visible { + self.QueueWriteString(DECTCEM.EscapeCodeToSet()) + } else { + self.QueueWriteString(DECTCEM.EscapeCodeToReset()) + } +} + func (self *Loop) MoveCursorHorizontally(amt int) { if amt != 0 { suffix := "C"