The broadcast kitten

Fixes #1569
This commit is contained in:
Kovid Goyal 2020-12-01 13:11:32 +05:30
parent 4a049b14ca
commit f481c17732
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 142 additions and 1 deletions

View File

@ -7,6 +7,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
0.19.3 [future]
-------------------
- A new :doc:`broadcast <kittens/broadcast>` kitten to type in all kitty windows
simultaneously (:iss:`1569`)
- Add a new mappable `select_tab` action to choose a tab to switch to even
when the tab bar is hidden (:iss:`3115`)

View File

@ -0,0 +1,23 @@
broadcast - type text in all kitty windows
==================================================
The ``broadcast`` kitten can be used to type text simultaneously in
all kitty windows (or a subset as desired).
To use it, simply create a mapping in :file:`kitty.conf` such as::
map F1 launch --allow-remote-control kitty +kitten broadcast
Then press the :kbd:`F1` key and whatever you type in the newly created widow
will be sent to all kitty windows.
You can use the options described below to control which windows
are selected.
.. program:: kitty +kitten broadcast
Command Line Interface
--------------------------
.. include:: /generated/cli-kitten-broadcast.rst

View File

@ -148,6 +148,19 @@ as the syntax for what follows :code:`kitty @` above. You do not need
to enable remote control to use these mappings.
Broadcasting what you type to all kitty windows
--------------------------------------------------
As a simple illustration of the power of remote control, lets
have what we type sent to all open kitty windows. To do that define the
following mapping in :file:`kitty.conf`::
map F1 launch --allow-remote-control kitty +kitten broadcast
Now press, F1 and start typing, what you type will be sent to all windows,
live, as you type it.
Documentation for the remote control protocol
-----------------------------------------------

View File

99
kittens/broadcast/main.py Normal file
View File

@ -0,0 +1,99 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
import sys
from base64 import standard_b64encode
from gettext import gettext as _
from typing import Any, Dict, List, Optional, Tuple
from kitty.cli import parse_args
from kitty.cli_stub import BroadcastCLIOptions
from kitty.key_encoding import RELEASE, key_defs as K
from kitty.rc.base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION
from kitty.remote_control import create_basic_command, encode_send
from kitty.typing import KeyEventType
from ..tui.handler import Handler
from ..tui.loop import Loop
from ..tui.operations import styled
class Broadcast(Handler):
def __init__(self, opts: BroadcastCLIOptions, initial_strings: List[str]) -> None:
self.opts = opts
self.initial_strings = initial_strings
self.payload = {'exclude_active': True, 'data': '', 'match': opts.match_tab, 'match_tab': opts.match_tab}
if not opts.match and not opts.match_tab:
self.payload['all'] = True
def initialize(self) -> None:
self.print('Type the text to broadcast below, press', styled('Ctrl+c', fg='yellow'), 'to quit:')
for x in self.initial_strings:
self.write_broadcast_text(x)
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
self.write_broadcast_text(text)
self.write(text)
def on_interrupt(self) -> None:
self.quit_loop(0)
def on_eot(self) -> None:
self.write_broadcast_text('\x04')
def on_key(self, key_event: KeyEventType) -> None:
if key_event.type is not RELEASE and not key_event.mods:
if key_event.key is K['TAB']:
self.write_broadcast_text('\t')
self.write('\t')
elif key_event.key is K['BACKSPACE']:
self.write_broadcast_text('\177')
self.write('\x08\x1b[X')
elif key_event.key is K['ENTER']:
self.write_broadcast_text('\r')
self.print('')
def write_broadcast_text(self, text: str) -> None:
self.write_broadcast_data('base64:' + standard_b64encode(text.encode('utf-8')).decode('ascii'))
def write_broadcast_data(self, data: str) -> None:
payload = self.payload.copy()
payload['data'] = data
send = create_basic_command('send-text', payload, no_response=True)
self.write(encode_send(send))
OPTIONS = (MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')).format
help_text = 'Broadcast typed text to all kitty windows. By default text is sent to all windows, unless one of the matching options is specified'
usage = '[initial text to send ...]'
def parse_broadcast_args(args: List[str]) -> Tuple[BroadcastCLIOptions, List[str]]:
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten broadcast', result_class=BroadcastCLIOptions)
def main(args: List[str]) -> Optional[Dict[str, Any]]:
try:
opts, items = parse_broadcast_args(args[1:])
except SystemExit as e:
if e.code != 0:
print(e.args[0], file=sys.stderr)
input(_('Press Enter to quit'))
return None
print('Type text to be broadcast below, Ctrl-C to quit:', end='\r\n')
sys.stdout.flush()
loop = Loop()
handler = Broadcast(opts, items)
loop.loop(handler)
if __name__ == '__main__':
main(sys.argv)
elif __name__ == '__doc__':
cd = sys.cli_docs # type: ignore
cd['usage'] = usage
cd['options'] = OPTIONS
cd['help_text'] = help_text

View File

@ -13,7 +13,7 @@ class CLIOptions:
LaunchCLIOptions = AskCLIOptions = ClipboardCLIOptions = DiffCLIOptions = CLIOptions
HintsCLIOptions = IcatCLIOptions = PanelCLIOptions = ResizeCLIOptions = CLIOptions
ErrorCLIOptions = UnicodeCLIOptions = RCOptions = RemoteFileCLIOptions = CLIOptions
QueryTerminalCLIOptions = CLIOptions
QueryTerminalCLIOptions = BroadcastCLIOptions = CLIOptions
def generate_stub() -> None:
@ -48,6 +48,9 @@ def do(otext=None, cls: str = 'CLIOptions', extra_fields: Sequence[str] = ()):
from kittens.hints.main import OPTIONS
do(OPTIONS(), 'HintsCLIOptions')
from kittens.broadcast.main import OPTIONS
do(OPTIONS(), 'BroadcastCLIOptions')
from kittens.icat.main import options_spec
do(options_spec(), 'IcatCLIOptions')