mirror of
https://github.com/kovidgoyal/kitty.git
synced 2025-12-13 20:36:22 +01:00
794 lines
29 KiB
Python
794 lines
29 KiB
Python
#!/usr/bin/env python
|
|
# License: GPLv3 Copyright: 2025, Kovid Goyal <kovid at kovidgoyal.net>
|
|
|
|
# This module must be runnable by a vanilla python interpreter
|
|
# as it is used to generate C code when building kitty
|
|
|
|
import re
|
|
import sys
|
|
from enum import Enum, auto
|
|
from functools import lru_cache
|
|
from typing import Any, Iterator, NamedTuple, Sequence
|
|
|
|
if getattr(sys, 'running_from_setup', False):
|
|
is_macos = 'darwin' in sys.platform.lower()
|
|
from shlex import split as psplit
|
|
|
|
def shlex_split(text: str) -> Iterator[str]:
|
|
yield from psplit(text)
|
|
else:
|
|
from kitty.constants import appname, is_macos
|
|
from kitty.utils import shlex_split as ksplit
|
|
def shlex_split(text: str) -> Iterator[str]:
|
|
yield from ksplit(text)
|
|
|
|
|
|
def serialize_as_go_string(x: str) -> str:
|
|
return x.replace('\\', '\\\\').replace('\n', '\\n').replace('"', '\\"')
|
|
|
|
|
|
class CompletionType(Enum):
|
|
file = auto()
|
|
directory = auto()
|
|
keyword = auto()
|
|
special = auto()
|
|
none = auto()
|
|
|
|
|
|
class CompletionRelativeTo(Enum):
|
|
cwd = auto()
|
|
config_dir = auto()
|
|
|
|
|
|
class CompletionSpec(NamedTuple):
|
|
|
|
type: CompletionType = CompletionType.none
|
|
kwds: tuple[str,...] = ()
|
|
extensions: tuple[str,...] = ()
|
|
mime_patterns: tuple[str,...] = ()
|
|
group: str = ''
|
|
relative_to: CompletionRelativeTo = CompletionRelativeTo.cwd
|
|
|
|
@staticmethod
|
|
def from_string(raw: str) -> 'CompletionSpec':
|
|
typ = CompletionType.none
|
|
kwds: tuple[str, ...] = ()
|
|
extensions: tuple[str, ...] = ()
|
|
mime_patterns: tuple[str, ...] = ()
|
|
group = ''
|
|
relative_to = CompletionRelativeTo.cwd
|
|
for x in shlex_split(raw):
|
|
ck, vv = x.split(':', 1)
|
|
if ck == 'type':
|
|
typ = getattr(CompletionType, vv)
|
|
elif ck == 'kwds':
|
|
kwds += tuple(vv.split(','))
|
|
elif ck == 'ext':
|
|
extensions += tuple(vv.split(','))
|
|
elif ck == 'group':
|
|
group = vv
|
|
elif ck == 'mime':
|
|
mime_patterns += tuple(vv.split(','))
|
|
elif ck == 'relative':
|
|
if vv == 'conf':
|
|
relative_to = CompletionRelativeTo.config_dir
|
|
else:
|
|
raise ValueError(f'Unknown completion relative to value: {vv}')
|
|
else:
|
|
raise KeyError(f'Unknown completion property: {ck}')
|
|
return CompletionSpec(
|
|
type=typ, kwds=kwds, extensions=extensions, mime_patterns=mime_patterns, group=group, relative_to=relative_to)
|
|
|
|
def as_go_code(self, go_name: str, sep: str = ': ') -> Iterator[str]:
|
|
completers = []
|
|
if self.kwds:
|
|
kwds = (f'"{serialize_as_go_string(x)}"' for x in self.kwds)
|
|
g = (self.group if self.type is CompletionType.keyword else '') or "Keywords"
|
|
completers.append(f'cli.NamesCompleter("{serialize_as_go_string(g)}", ' + ', '.join(kwds) + ')')
|
|
relative_to = 'CONFIG' if self.relative_to is CompletionRelativeTo.config_dir else 'CWD'
|
|
if self.type is CompletionType.file:
|
|
g = serialize_as_go_string(self.group or 'Files')
|
|
added = False
|
|
if self.extensions:
|
|
added = True
|
|
pats = (f'"*.{ext}"' for ext in self.extensions)
|
|
completers.append(f'cli.FnmatchCompleter("{g}", cli.{relative_to}, ' + ', '.join(pats) + ')')
|
|
if self.mime_patterns:
|
|
added = True
|
|
completers.append(f'cli.MimepatCompleter("{g}", cli.{relative_to}, ' + ', '.join(f'"{p}"' for p in self.mime_patterns) + ')')
|
|
if not added:
|
|
completers.append(f'cli.FnmatchCompleter("{g}", cli.{relative_to}, "*")')
|
|
if self.type is CompletionType.directory:
|
|
g = serialize_as_go_string(self.group or 'Directories')
|
|
completers.append(f'cli.DirectoryCompleter("{g}", cli.{relative_to})')
|
|
if self.type is CompletionType.special:
|
|
completers.append(self.group)
|
|
if len(completers) > 1:
|
|
yield f'{go_name}{sep}cli.ChainCompleters(' + ', '.join(completers) + ')'
|
|
elif completers:
|
|
yield f'{go_name}{sep}{completers[0]}'
|
|
|
|
|
|
class OptionDefinition(NamedTuple):
|
|
dest: str = ''
|
|
name: str = ''
|
|
aliases: tuple[str, ...] = ()
|
|
help: str = ''
|
|
choices: tuple[str, ...] = ()
|
|
type: str = ''
|
|
default: str | None = None
|
|
condition: bool = False
|
|
completion: CompletionSpec = CompletionSpec()
|
|
|
|
|
|
|
|
OptionSpecSeq = Sequence[str | OptionDefinition]
|
|
|
|
|
|
@lru_cache(64)
|
|
def parse_option_spec(spec: str | None = None) -> tuple[OptionSpecSeq, OptionSpecSeq]:
|
|
if spec is None:
|
|
spec = kitty_options_spec()
|
|
NORMAL, METADATA, HELP = 'NORMAL', 'METADATA', 'HELP'
|
|
state = NORMAL
|
|
lines = spec.splitlines()
|
|
prev_line = ''
|
|
prev_indent = 0
|
|
seq: list[str | OptionDefinition] = []
|
|
disabled: list[str | OptionDefinition] = []
|
|
mpat = re.compile('([a-z]+)=(.+)')
|
|
current_cmd = empty_cmd = OptionDefinition()
|
|
|
|
def indent_of_line(x: str) -> int:
|
|
return len(x) - len(x.lstrip())
|
|
|
|
for line in lines:
|
|
line = line.rstrip()
|
|
if state is NORMAL:
|
|
if not line:
|
|
continue
|
|
if line.startswith('# '):
|
|
seq.append(line[2:])
|
|
continue
|
|
if line.startswith('--'):
|
|
parts = line.split(' ')
|
|
defdest = parts[0][2:].replace('-', '_')
|
|
current_cmd = OptionDefinition(dest=defdest, aliases=tuple(parts), name=defdest, condition=True)
|
|
state = METADATA
|
|
continue
|
|
raise ValueError(f'Invalid option spec, unexpected line: {line}')
|
|
elif state is METADATA:
|
|
m = mpat.match(line)
|
|
if m is None:
|
|
state = HELP
|
|
current_cmd = current_cmd._replace(help=current_cmd.help + line)
|
|
else:
|
|
k, v = m.group(1), m.group(2)
|
|
if k == 'choices':
|
|
vals = tuple(x.strip() for x in v.split(','))
|
|
if not current_cmd.type:
|
|
current_cmd = current_cmd._replace(type='choices')
|
|
if current_cmd.type != 'choices':
|
|
raise ValueError(f'Cannot specify choices for an option of type: {current_cmd.type}')
|
|
current_cmd = current_cmd._replace(choices=tuple(vals))
|
|
if current_cmd.default is None:
|
|
current_cmd = current_cmd._replace(default=vals[0])
|
|
else:
|
|
if k == 'default':
|
|
current_cmd = current_cmd._replace(default=v)
|
|
elif k == 'type':
|
|
if v == 'choice':
|
|
v = 'choices'
|
|
current_cmd = current_cmd._replace(type=v)
|
|
elif k == 'dest':
|
|
current_cmd = current_cmd._replace(dest=v)
|
|
elif k == 'condition':
|
|
current_cmd = current_cmd._replace(condition=bool(eval(v)))
|
|
elif k == 'completion':
|
|
current_cmd = current_cmd._replace(completion=CompletionSpec.from_string(v))
|
|
elif state is HELP:
|
|
if line:
|
|
current_indent = indent_of_line(line)
|
|
if current_indent > 1:
|
|
if prev_indent == 0:
|
|
current_cmd = current_cmd._replace(help=current_cmd.help + '\n')
|
|
else:
|
|
line = line.strip()
|
|
prev_indent = current_indent
|
|
spc = '' if current_cmd.help.endswith('\n') else ' '
|
|
current_cmd = current_cmd._replace(help=current_cmd.help + spc + line)
|
|
else:
|
|
prev_indent = 0
|
|
if prev_line:
|
|
h = '\n' if current_cmd.help.endswith('::') else '\n\n'
|
|
current_cmd = current_cmd._replace(help=current_cmd.help + h)
|
|
else:
|
|
state = NORMAL
|
|
(seq if current_cmd.condition else disabled).append(current_cmd)
|
|
current_cmd = empty_cmd
|
|
prev_line = line
|
|
if current_cmd is not empty_cmd:
|
|
(seq if current_cmd.condition else disabled).append(current_cmd)
|
|
|
|
return seq, disabled
|
|
|
|
|
|
def defval_for_opt(opt: OptionDefinition) -> Any:
|
|
dv: Any = opt.default
|
|
typ = opt.type
|
|
if typ.startswith('bool-'):
|
|
if dv is None:
|
|
dv = False if typ == 'bool-set' else True
|
|
else:
|
|
dv = dv.lower() in ('true', 'yes', 'y')
|
|
elif typ == 'list':
|
|
dv = list(shlex_split(dv)) if dv else []
|
|
elif typ in ('int', 'float'):
|
|
dv = (int if typ == 'int' else float)(dv or 0)
|
|
return dv
|
|
|
|
|
|
def get_option_maps(seq: OptionSpecSeq) -> tuple[dict[str, OptionDefinition], dict[str, OptionDefinition], dict[str, Any]]:
|
|
names_map: dict[str, OptionDefinition] = {}
|
|
alias_map: dict[str, OptionDefinition] = {}
|
|
values_map: dict[str, Any] = {}
|
|
for opt in seq:
|
|
if isinstance(opt, str):
|
|
continue
|
|
for alias in opt.aliases:
|
|
alias_map[alias] = opt
|
|
name = opt.dest
|
|
names_map[name] = opt
|
|
values_map[name] = defval_for_opt(opt)
|
|
return names_map, alias_map, values_map
|
|
|
|
|
|
def c_str(x: str) -> str:
|
|
x = x.replace('\\', r'\\')
|
|
return f'"{x}"'
|
|
|
|
|
|
def add_list_values(*values: str) -> Iterator[str]:
|
|
yield f'\tflag.defval.listval.items = alloc_for_cli(spec, {len(values)} * sizeof(flag.defval.listval.items[0]));'
|
|
yield '\tif (!flag.defval.listval.items) OOM;'
|
|
yield f'\tflag.defval.listval.count = {len(values)};'
|
|
yield f'\tflag.defval.listval.capacity = {len(values)};'
|
|
for n, value in enumerate(values):
|
|
yield f'\tflag.defval.listval.items[{n}] = {c_str(value)};'
|
|
|
|
|
|
def generate_c_for_opt(name: str, defval: Any, opt: OptionDefinition) -> Iterator[str]:
|
|
yield f'\tflag = (FlagSpec){{.dest={c_str(name)},}};'
|
|
match opt.type:
|
|
case 'bool-set' | 'bool-reset':
|
|
yield '\tflag.defval.type = CLI_VALUE_BOOL;'
|
|
yield f'\tflag.defval.boolval = {"true" if defval else "false"};'
|
|
case 'int':
|
|
yield '\tflag.defval.type = CLI_VALUE_INT;'
|
|
yield f'\tflag.defval.intval = {defval};'
|
|
case 'float':
|
|
yield '\tflag.defval.type = CLI_VALUE_FLOAT;'
|
|
yield f'\tflag.defval.floatval = {defval};'
|
|
case 'list':
|
|
yield '\tflag.defval.type = CLI_VALUE_LIST;'
|
|
if defval:
|
|
yield from add_list_values(*defval)
|
|
case 'choices':
|
|
yield '\tflag.defval.type = CLI_VALUE_CHOICE;'
|
|
yield f'\tflag.defval.strval = {c_str(defval)};'
|
|
yield from add_list_values(*opt.choices)
|
|
case _:
|
|
yield '\tflag.defval.type = CLI_VALUE_STRING;'
|
|
if defval is not None:
|
|
yield f'\tflag.defval.strval = {c_str(defval)};'
|
|
|
|
|
|
def generate_c_parser_for(funcname: str, spec: str) -> Iterator[str]:
|
|
seq, disabled = parse_option_spec(spec)
|
|
names_map, _, defaults_map = get_option_maps(seq)
|
|
if 'help' not in names_map:
|
|
names_map['help'] = OptionDefinition(type='bool-set', aliases=('--help', '-h'))
|
|
defaults_map['help'] = False
|
|
if 'version' not in names_map:
|
|
names_map['version'] = OptionDefinition(type='bool-set', aliases=('--version', '-v'))
|
|
defaults_map['version'] = False
|
|
|
|
yield f'static void\nparse_cli_for_{funcname}(CLISpec *spec, int argc, char **argv) {{' # }}
|
|
yield '\tFlagSpec flag;'
|
|
for name, opt in names_map.items():
|
|
for alias in opt.aliases:
|
|
yield f'\tif (vt_is_end(vt_insert(&spec->alias_map, {c_str(alias)}, {c_str(name)}))) OOM;'
|
|
yield from generate_c_for_opt(name, defaults_map[name], opt)
|
|
yield '\tif (vt_is_end(vt_insert(&spec->flag_map, flag.dest, flag))) OOM;'
|
|
for d in disabled:
|
|
if not isinstance(d, str):
|
|
yield from generate_c_for_opt(d.dest, defval_for_opt(d), d)
|
|
yield '\tif (vt_is_end(vt_insert(&spec->disabled_map, flag.dest, flag))) OOM;'
|
|
|
|
yield '\tparse_cli_loop(spec, true, argc, argv);'
|
|
yield '}'
|
|
|
|
|
|
def generate_c_parsers() -> Iterator[str]:
|
|
yield '#pragma once'
|
|
yield '// generated by simple_cli_definitions.py do NOT edit!'
|
|
yield '#include "cli-parser.h"'
|
|
yield from generate_c_parser_for('kitty', kitty_options_spec())
|
|
yield ''
|
|
yield ''
|
|
yield from generate_c_parser_for('panel_kitten', build_panel_cli_spec({}))
|
|
yield ''
|
|
|
|
|
|
# kitty CLI spec {{{
|
|
grab_keyboard_docs = """\
|
|
Grab the keyboard. This means global shortcuts defined in the OS will be passed to kitty instead. Useful if
|
|
you want to create an OS modal window. How well this
|
|
works depends on the OS/window manager/desktop environment. On Wayland it works only if the compositor implements
|
|
the :link:`inhibit-keyboard-shortcuts protocol <https://wayland.app/protocols/keyboard-shortcuts-inhibit-unstable-v1>`.
|
|
On macOS Apple doesn't allow applications to grab the keyboard without special permissions, so it doesn't work.
|
|
"""
|
|
|
|
listen_on_defn = f'''\
|
|
--listen-on
|
|
completion=type:special group:complete_kitty_listen_on
|
|
Listen on the specified socket address for control messages. For example,
|
|
:option:`{appname} --listen-on`=unix:/tmp/mykitty or :option:`{appname}
|
|
--listen-on`=tcp:localhost:12345. On Linux systems, you can also use abstract
|
|
UNIX sockets, not associated with a file, like this: :option:`{appname}
|
|
--listen-on`=unix:@mykitty. Environment variables are expanded and relative
|
|
paths are resolved with respect to the temporary directory. To control kitty,
|
|
you can send commands to it with :italic:`kitten @` using the
|
|
:option:`kitten @ --to` option to specify this address. Note that if you run
|
|
:italic:`kitten @` within a kitty window, there is no need to specify the
|
|
:option:`kitten @ --to` option as it will automatically read from the
|
|
environment. Note that this will be ignored unless :opt:`allow_remote_control`
|
|
is set to either: :code:`yes`, :code:`socket` or :code:`socket-only`. This can
|
|
also be specified in :file:`kitty.conf`.
|
|
'''
|
|
|
|
wait_for_single_instance_defn = f'''\
|
|
--wait-for-single-instance-window-close
|
|
type=bool-set
|
|
Normally, when using :option:`{appname} --single-instance`, :italic:`{appname}`
|
|
will open a new window in an existing instance and quit immediately. With this
|
|
option, it will not quit till the newly opened window is closed. Note that if no
|
|
previous instance is found, then :italic:`{appname}` will wait anyway,
|
|
regardless of this option.
|
|
'''
|
|
|
|
CONFIG_HELP = '''\
|
|
Specify a path to the configuration file(s) to use. All configuration files are
|
|
merged onto the builtin :file:`{conf_name}.conf`, overriding the builtin values.
|
|
This option can be specified multiple times to read multiple configuration files
|
|
in sequence, which are merged. Use the special value :code:`NONE` to not load
|
|
any config file.
|
|
|
|
If this option is not specified, config files are searched for in the order:
|
|
:file:`$XDG_CONFIG_HOME/{appname}/{conf_name}.conf`,
|
|
:file:`~/.config/{appname}/{conf_name}.conf`,{macos_confpath}
|
|
:file:`$XDG_CONFIG_DIRS/{appname}/{conf_name}.conf`. The first one that exists
|
|
is used as the config file.
|
|
|
|
If the environment variable :envvar:`KITTY_CONFIG_DIRECTORY` is specified, that
|
|
directory is always used and the above searching does not happen.
|
|
|
|
If :file:`/etc/xdg/{appname}/{conf_name}.conf` exists, it is merged before (i.e.
|
|
with lower priority) than any user config files. It can be used to specify
|
|
system-wide defaults for all users. You can use either :code:`-` or
|
|
:file:`/dev/stdin` to read the config from STDIN.
|
|
'''.replace(
|
|
'{macos_confpath}',
|
|
(' :file:`~/Library/Preferences/{appname}/{conf_name}.conf`,' if is_macos else ''), 1
|
|
)
|
|
|
|
|
|
def kitty_options_spec() -> str:
|
|
if not hasattr(kitty_options_spec, 'ans'):
|
|
OPTIONS = """
|
|
--class --app-id
|
|
dest=cls
|
|
default={appname}
|
|
condition=not is_macos
|
|
On Wayland set the :italic:`application id`. On X11 set the class part of the :italic:`WM_CLASS` window property.
|
|
|
|
|
|
--name --os-window-tag
|
|
condition=not is_macos
|
|
On Wayland, set the :italic:`window tag`, when specified.
|
|
On X11, set the name part of the :italic:`WM_CLASS` property, when unset, defaults to using the
|
|
value from :option:`{appname} --class`.
|
|
|
|
|
|
--title -T
|
|
Set the OS window title. This will override any title set by the program running
|
|
inside kitty, permanently fixing the OS window's title. So only use this if you
|
|
are running a program that does not set titles.
|
|
|
|
|
|
--config -c
|
|
type=list
|
|
completion=type:file ext:conf group:"Config files" kwds:none,NONE
|
|
{config_help}
|
|
|
|
|
|
--override -o
|
|
type=list
|
|
completion=type:special group:complete_kitty_override
|
|
Override individual configuration options, can be specified multiple times.
|
|
Syntax: :italic:`name=value`. For example: :option:`{appname} -o` font_size=20
|
|
|
|
|
|
--directory --working-directory -d
|
|
default=.
|
|
completion=type:directory
|
|
Change to the specified directory when launching.
|
|
|
|
|
|
--detach
|
|
type=bool-set
|
|
Detach from the controlling terminal, if any. On macOS
|
|
use :code:`open -a kitty.app -n` instead.
|
|
|
|
|
|
--detached-log
|
|
Path to a log file to store STDOUT/STDERR when using :option:`--detach`
|
|
|
|
|
|
--session
|
|
completion=type:file ext:session relative:conf group:"Session files"
|
|
Path to a file containing the startup :italic:`session` (tabs, windows, layout,
|
|
programs). Use - to read from STDIN. See :ref:`sessions` for details and
|
|
an example. Environment variables in the file name are expanded,
|
|
relative paths are resolved relative to the kitty configuration directory.
|
|
The special value :code:`none` means no session will be used, even if
|
|
the :opt:`startup_session` option has been specified in kitty.conf.
|
|
Note that using this option means the command line arguments to kitty specifying
|
|
a program to run are ignored.
|
|
|
|
|
|
--hold
|
|
type=bool-set
|
|
Remain open, at a shell prompt, after child process exits. Note that this only
|
|
affects the first window. You can quit by either using the close window
|
|
shortcut or running the exit command.
|
|
|
|
|
|
--single-instance -1
|
|
type=bool-set
|
|
If specified only a single instance of :italic:`{appname}` will run. New
|
|
invocations will instead create a new top-level window in the existing
|
|
:italic:`{appname}` instance. This allows :italic:`{appname}` to share a single
|
|
sprite cache on the GPU and also reduces startup time. You can also have
|
|
separate groups of :italic:`{appname}` instances by using the :option:`{appname}
|
|
--instance-group` option.
|
|
|
|
|
|
--instance-group
|
|
Used in combination with the :option:`{appname} --single-instance` option. All
|
|
:italic:`{appname}` invocations with the same :option:`{appname}
|
|
--instance-group` will result in new windows being created in the first
|
|
:italic:`{appname}` instance within that group.
|
|
|
|
|
|
{wait_for_single_instance_defn}
|
|
|
|
|
|
{listen_on_defn} To start in headless mode,
|
|
without an actual window, use :option:`{appname} --start-as`=hidden.
|
|
|
|
|
|
--start-as
|
|
type=choices
|
|
default=normal
|
|
choices=normal,fullscreen,maximized,minimized,hidden
|
|
Control how the initial kitty OS window is created. Note that
|
|
this is applies to all OS Windows if you use the :option:`{appname} --session`
|
|
option to create multiple OS Windows. Any OS Windows state
|
|
specified in the session file gets overriden.
|
|
|
|
|
|
--position
|
|
The position, for example 10x20, on screen at which to place the first kitty OS Window.
|
|
This may or may not work depending on the policies of the desktop
|
|
environment/window manager. It never works on Wayland.
|
|
See also :opt:`remember_window_position` to have kitty automatically try
|
|
to restore the previous window position.
|
|
|
|
|
|
--grab-keyboard
|
|
type=bool-set
|
|
{grab_keyboard_docs}
|
|
|
|
|
|
# Debugging options
|
|
|
|
--version -v
|
|
type=bool-set
|
|
The current {appname} version.
|
|
|
|
|
|
--dump-commands
|
|
type=bool-set
|
|
Output commands received from child process to STDOUT.
|
|
|
|
|
|
--replay-commands
|
|
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"
|
|
|
|
|
|
--dump-bytes
|
|
Path to file in which to store the raw bytes received from the child process.
|
|
|
|
|
|
--debug-rendering --debug-gl
|
|
type=bool-set
|
|
Debug rendering commands. This will cause all OpenGL calls to check for errors
|
|
instead of ignoring them. Also prints out miscellaneous debug information.
|
|
Useful when debugging rendering problems.
|
|
|
|
|
|
--debug-input --debug-keyboard
|
|
dest=debug_keyboard
|
|
type=bool-set
|
|
Print out key and mouse events as they are received.
|
|
|
|
|
|
--debug-font-fallback
|
|
type=bool-set
|
|
Print out information about the selection of fallback fonts for characters not
|
|
present in the main font.
|
|
|
|
|
|
--watcher
|
|
completion=type:file ext:py relative:conf group:"Watcher files"
|
|
This option is deprecated in favor of the :opt:`watcher` option in
|
|
:file:`{conf_name}.conf` and should not be used.
|
|
|
|
|
|
--execute -e
|
|
type=bool-set
|
|
!
|
|
"""
|
|
setattr(kitty_options_spec, 'ans', OPTIONS.format(
|
|
appname=appname, conf_name=appname, listen_on_defn=listen_on_defn,
|
|
grab_keyboard_docs=grab_keyboard_docs, wait_for_single_instance_defn=wait_for_single_instance_defn,
|
|
config_help=CONFIG_HELP.format(appname=appname, conf_name=appname
|
|
)))
|
|
ans: str = getattr(kitty_options_spec, 'ans')
|
|
return ans
|
|
# }}}
|
|
|
|
|
|
# panel CLI spec {{{
|
|
panel_defaults = {
|
|
'lines': '1', 'columns': '1',
|
|
'margin_left': '0', 'margin_top': '0', 'margin_right': '0', 'margin_bottom': '0',
|
|
'edge': 'top', 'layer': 'bottom', 'override': '', 'cls': f'{appname}-panel',
|
|
'focus_policy': 'not-allowed', 'exclusive_zone': '-1', 'override_exclusive_zone': 'no',
|
|
'single_instance': 'no', 'instance_group': '', 'toggle_visibility': 'no',
|
|
'start_as_hidden': 'no', 'detach': 'no', 'detached_log': '',
|
|
}
|
|
|
|
def build_panel_cli_spec(defaults: dict[str, str]) -> str:
|
|
d = panel_defaults.copy()
|
|
d.update(defaults)
|
|
return r'''
|
|
--lines
|
|
default={lines}
|
|
The number of lines shown in the panel. Ignored for background, centered, and vertical panels.
|
|
If it has the suffix :code:`px` then it sets the height of the panel in pixels instead of lines.
|
|
|
|
|
|
--columns
|
|
default={columns}
|
|
The number of columns shown in the panel. Ignored for background, centered, and horizontal panels.
|
|
If it has the suffix :code:`px` then it sets the width of the panel in pixels instead of columns.
|
|
|
|
|
|
--margin-top
|
|
type=int
|
|
default={margin_top}
|
|
Set the top margin for the panel, in pixels. Has no effect for bottom edge panels.
|
|
Only works on macOS and Wayland compositors that supports the wlr layer shell protocol.
|
|
|
|
|
|
--margin-left
|
|
type=int
|
|
default={margin_left}
|
|
Set the left margin for the panel, in pixels. Has no effect for right edge panels.
|
|
Only works on macOS and Wayland compositors that supports the wlr layer shell protocol.
|
|
|
|
|
|
--margin-bottom
|
|
type=int
|
|
default={margin_bottom}
|
|
Set the bottom margin for the panel, in pixels. Has no effect for top edge panels.
|
|
Only works on macOS and Wayland compositors that supports the wlr layer shell protocol.
|
|
|
|
|
|
--margin-right
|
|
type=int
|
|
default={margin_right}
|
|
Set the right margin for the panel, in pixels. Has no effect for left edge panels.
|
|
Only works on macOS and Wayland compositors that supports the wlr layer shell protocol.
|
|
|
|
|
|
--edge
|
|
choices=top,bottom,left,right,background,center,center-sized,none
|
|
default={edge}
|
|
Which edge of the screen to place the panel on. Note that some window managers
|
|
(such as i3) do not support placing docked windows on the left and right edges.
|
|
The value :code:`background` means make the panel the "desktop wallpaper".
|
|
Note that when using sway if you set a background in your sway config it will
|
|
cover the background drawn using this kitten.
|
|
Additionally, there are three more values: :code:`center`, :code:`center-sized` and :code:`none`.
|
|
The value :code:`center` anchors the panel to all sides and covers the entire
|
|
display (on macOS the part of the display not covered by titlebar and dock).
|
|
The panel can be shrunk and placed using the margin parameters.
|
|
The value :code:`none` anchors the panel to the top left corner and should be
|
|
placed using the margin parameters. Its size is set by :option:`--lines`
|
|
and :option:`--columns`. The value :code:`center-sized` is just like :code:`none` except
|
|
that the panel is centered instead of in the top left corner and the margins have no effect.
|
|
|
|
|
|
--layer
|
|
choices=background,bottom,top,overlay
|
|
default={layer}
|
|
On a Wayland compositor that supports the wlr layer shell protocol, specifies the layer
|
|
on which the panel should be drawn. This parameter is ignored and set to
|
|
:code:`background` if :option:`--edge` is set to :code:`background`. On macOS, maps
|
|
these to appropriate NSWindow *levels*.
|
|
|
|
|
|
--config -c
|
|
type=list
|
|
Path to config file to use for kitty when drawing the panel.
|
|
|
|
|
|
--override -o
|
|
type=list
|
|
default={override}
|
|
Override individual kitty configuration options, can be specified multiple times.
|
|
Syntax: :italic:`name=value`. For example: :option:`kitty +kitten panel -o` font_size=20
|
|
|
|
|
|
--output-name
|
|
The panel can only be displayed on a single monitor (output) at a time. This allows
|
|
you to specify which output is used, by name. If not specified the compositor will choose an
|
|
output automatically, typically the last output the user interacted with or the primary monitor.
|
|
Use the special value :code:`list` to get a list of available outputs. Use :code:`listjson` for
|
|
a json encoded output. Note that on Wayland the output can only be set at panel creation time,
|
|
it cannot be changed after creation, nor is there anyway to display a single panel on all outputs.
|
|
Please complain to the Wayland developers about this.
|
|
|
|
|
|
--class --app-id
|
|
dest=cls
|
|
default={cls}
|
|
condition=not is_macos
|
|
On Wayland set the :italic:`namespace` of the layer shell surface. On X11 set the class part of the :italic:`WM_CLASS` window property.
|
|
|
|
|
|
--name --os-window-tag
|
|
condition=not is_macos
|
|
On X11 sets the name part of the :italic:`WM_CLASS` property on X11,
|
|
when unspecified uses the value from :option:`{appname} --class` on X11.
|
|
|
|
|
|
--focus-policy
|
|
choices=not-allowed,exclusive,on-demand
|
|
default={focus_policy}
|
|
On a Wayland compositor that supports the wlr layer shell protocol, specify the focus policy for keyboard
|
|
interactivity with the panel. Please refer to the wlr layer shell protocol documentation for more details.
|
|
Note that different Wayland compositors behave very differently with :code:`exclusive`, your mileage may vary.
|
|
On macOS, :code:`exclusive` and :code:`on-demand` are currently the same.
|
|
|
|
|
|
--hide-on-focus-loss
|
|
type=bool-set
|
|
Automatically hide the panel window when it loses focus. Using this option will force :option:`--focus-policy`
|
|
to :code:`on-demand`. Note that on Wayland, depending on the compositor, this can result in the window never
|
|
becoming visible.
|
|
|
|
|
|
--grab-keyboard
|
|
type=bool-set
|
|
{grab_keyboard_docs}
|
|
|
|
|
|
--exclusive-zone
|
|
type=int
|
|
default={exclusive_zone}
|
|
On a Wayland compositor that supports the wlr layer shell protocol, request a given exclusive zone for the panel.
|
|
Please refer to the wlr layer shell documentation for more details on the meaning of exclusive and its value.
|
|
If :option:`--edge` is set to anything other than :code:`center` or :code:`none`, this flag will not have any
|
|
effect unless the flag :option:`--override-exclusive-zone` is also set.
|
|
If :option:`--edge` is set to :code:`background`, this option has no effect.
|
|
Ignored on X11 and macOS.
|
|
|
|
|
|
--override-exclusive-zone
|
|
type=bool-set
|
|
default={override_exclusive_zone}
|
|
On a Wayland compositor that supports the wlr layer shell protocol, override the default exclusive zone.
|
|
This has effect only if :option:`--edge` is set to :code:`top`, :code:`left`, :code:`bottom` or :code:`right`.
|
|
Ignored on X11 and macOS.
|
|
|
|
|
|
--single-instance -1
|
|
type=bool-set
|
|
default={single_instance}
|
|
If specified only a single instance of the panel will run. New
|
|
invocations will instead create a new top-level window in the existing
|
|
panel instance.
|
|
|
|
|
|
--instance-group
|
|
default={instance_group}
|
|
Used in combination with the :option:`--single-instance` option. All
|
|
panel invocations with the same :option:`--instance-group` will result
|
|
in new panels being created in the first panel instance within that group.
|
|
|
|
|
|
{wait_for_single_instance_defn}
|
|
|
|
|
|
{listen_on_defn}
|
|
|
|
|
|
--toggle-visibility
|
|
type=bool-set
|
|
default={toggle_visibility}
|
|
When set and using :option:`--single-instance` will toggle the visibility of the
|
|
existing panel rather than creating a new one.
|
|
|
|
|
|
--move-to-active-monitor
|
|
type=bool-set
|
|
default=false
|
|
When set and using :option:`--toggle-visibility` to show an existing panel, the panel
|
|
is moved to the active monitor (typically the monitor with the mouse on it).
|
|
This works only if the underlying OS supports it. It is currently supported on macOS only.
|
|
|
|
|
|
--start-as-hidden
|
|
type=bool-set
|
|
default={start_as_hidden}
|
|
Start in hidden mode, useful with :option:`--toggle-visibility`.
|
|
|
|
|
|
--detach
|
|
type=bool-set
|
|
default={detach}
|
|
Detach from the controlling terminal, if any, running in an independent child process,
|
|
the parent process exits immediately.
|
|
|
|
|
|
--detached-log
|
|
default={detached_log}
|
|
Path to a log file to store STDOUT/STDERR when using :option:`--detach`
|
|
|
|
|
|
--debug-rendering
|
|
type=bool-set
|
|
For internal debugging use.
|
|
|
|
|
|
--debug-input
|
|
type=bool-set
|
|
For internal debugging use.
|
|
'''.format(
|
|
appname=appname, listen_on_defn=listen_on_defn, grab_keyboard_docs=grab_keyboard_docs,
|
|
wait_for_single_instance_defn=wait_for_single_instance_defn, **d)
|
|
|
|
|
|
def panel_options_spec() -> str:
|
|
return build_panel_cli_spec(panel_defaults)
|
|
|
|
# }}}
|