mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-05-13 16:37:27 +00:00
Rework: background_image glob + change_background_image action + lazy loading
Reworked based on maintainer feedback: - background_image now accepts glob patterns (e.g., ~/backgrounds/*.png) that resolve to a sorted list of image paths - New change_background_image action: no arg = +1, +N/-N = delta with wraparound, plain N = absolute index clamped to list size - Lazy loading: images loaded on first access, cached in VRAM after - Failed image loads are silently removed from the list - kitten @ set-background-image extended: number or +/-N arguments cycle through the background image list for all matching OS windows - Per-window index tracking into shared image path list
This commit is contained in:
parent
019158c168
commit
99cc202aec
12 changed files with 414 additions and 4 deletions
|
|
@ -115,6 +115,8 @@ from .fast_data_types import (
|
|||
send_data_to_peer,
|
||||
set_application_quit_request,
|
||||
set_background_image,
|
||||
set_bg_image_paths,
|
||||
change_bg_image,
|
||||
set_boss,
|
||||
set_options,
|
||||
set_os_window_chrome,
|
||||
|
|
@ -1390,6 +1392,10 @@ class Boss:
|
|||
else:
|
||||
self.startup_first_child(first_os_window_id, startup_sessions=startup_sessions)
|
||||
|
||||
paths = getattr(get_options(), 'background_image_paths', ())
|
||||
if paths:
|
||||
set_bg_image_paths(list(paths))
|
||||
|
||||
if get_options().update_check_interval > 0 and not self.update_check_started and getattr(sys, 'frozen', False):
|
||||
from .update_check import run_update_check
|
||||
run_update_check(get_options().update_check_interval * 60 * 60)
|
||||
|
|
@ -3139,6 +3145,9 @@ class Boss:
|
|||
set_background_image(opts.background_image, tuple(self.os_window_map), True, opts.background_image_layout)
|
||||
except Exception as e:
|
||||
log_error(f'Failed to set background image with error: {e}')
|
||||
paths = getattr(opts, 'background_image_paths', ())
|
||||
if paths:
|
||||
set_bg_image_paths(list(paths))
|
||||
for tm in self.all_tab_managers:
|
||||
tm.apply_options()
|
||||
# Update colors
|
||||
|
|
@ -3541,6 +3550,19 @@ class Boss:
|
|||
) -> None:
|
||||
set_background_image(path, os_windows, configured, layout, png_data, linear_interpolation, tint, tint_gaps)
|
||||
|
||||
@ac('misc', 'Change the background image from the list of images matched by the :opt:`background_image` glob pattern')
|
||||
def change_background_image(self, delta: str = '+1') -> None:
|
||||
aw = self.active_window
|
||||
if aw is None:
|
||||
return
|
||||
try:
|
||||
if delta[0] in '+-':
|
||||
change_bg_image(aw.os_window_id, int(delta), True)
|
||||
else:
|
||||
change_bg_image(aw.os_window_id, int(delta), False)
|
||||
except (ValueError, IndexError):
|
||||
log_error(f'Invalid argument to change_background_image: {delta}')
|
||||
|
||||
# Can be called with kitty -o "map f1 send_test_notification"
|
||||
def send_test_notification(self) -> None:
|
||||
self.notification_manager.send_test_notification()
|
||||
|
|
|
|||
|
|
@ -732,6 +732,14 @@ def set_background_image(
|
|||
pass
|
||||
|
||||
|
||||
def set_bg_image_paths(paths: list[str]) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def change_bg_image(os_window_id: int, value: int, is_delta: bool) -> bool:
|
||||
pass
|
||||
|
||||
|
||||
def set_boss(boss: Boss) -> None:
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,12 @@ typedef struct {
|
|||
size_t mmap_size;
|
||||
} BackgroundImage;
|
||||
|
||||
typedef struct {
|
||||
char **paths;
|
||||
BackgroundImage **images;
|
||||
unsigned int count;
|
||||
} BackgroundImageList;
|
||||
|
||||
|
||||
#ifdef GRAPHICS_INTERNAL_APIS
|
||||
typedef struct {
|
||||
|
|
|
|||
|
|
@ -1957,7 +1957,9 @@ this option by reloading the config is not supported.
|
|||
|
||||
opt('background_image', 'none',
|
||||
option_type='config_or_absolute_path', ctype='!background_image',
|
||||
long_text='Path to a background image. Must be in PNG/JPEG/WEBP/TIFF/GIF/BMP format.'
|
||||
long_text='Path to a background image or a glob pattern matching multiple images. Must be in PNG/JPEG/WEBP/TIFF/GIF/BMP format.'
|
||||
' When a glob pattern matches multiple images, they are sorted lexically and the first is used.'
|
||||
' Use the :ac:`change_background_image` action to cycle through them.'
|
||||
' Note that when using :ref:`auto_color_scheme` this option is overridden by the color scheme file and must be set inside it to take effect.'
|
||||
)
|
||||
|
||||
|
|
|
|||
17
kitty/options/parse.py
generated
17
kitty/options/parse.py
generated
|
|
@ -77,7 +77,22 @@ class Parser:
|
|||
ans['background_blur'] = int(val)
|
||||
|
||||
def background_image(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
ans['background_image'] = config_or_absolute_path(val)
|
||||
import glob as glob_mod
|
||||
import os
|
||||
path = config_or_absolute_path(val)
|
||||
if path is None:
|
||||
ans['background_image'] = None
|
||||
ans['background_image_paths'] = ()
|
||||
return
|
||||
matches = sorted(glob_mod.glob(path))
|
||||
image_exts = {'.png', '.jpg', '.jpeg', '.webp', '.gif', '.bmp', '.tiff'}
|
||||
matches = [m for m in matches if os.path.isfile(m) and os.path.splitext(m)[1].lower() in image_exts]
|
||||
if matches:
|
||||
ans['background_image'] = matches[0]
|
||||
ans['background_image_paths'] = tuple(matches)
|
||||
else:
|
||||
ans['background_image'] = path
|
||||
ans['background_image_paths'] = (path,) if os.path.isfile(path) else ()
|
||||
|
||||
def background_image_layout(self, val: str, ans: dict[str, typing.Any]) -> None:
|
||||
val = val.lower()
|
||||
|
|
|
|||
2
kitty/options/types.py
generated
2
kitty/options/types.py
generated
|
|
@ -61,6 +61,7 @@ option_names = (
|
|||
'background_opacity',
|
||||
'background_tint',
|
||||
'background_tint_gaps',
|
||||
'background_image_paths',
|
||||
'bell_border_color',
|
||||
'bell_on_tab',
|
||||
'bell_path',
|
||||
|
|
@ -534,6 +535,7 @@ class Options:
|
|||
background_opacity: float = 1.0
|
||||
background_tint: float = 0
|
||||
background_tint_gaps: float = 1.0
|
||||
background_image_paths: tuple[str, ...] = ()
|
||||
bell_border_color: Color = Color(255, 90, 0)
|
||||
bell_on_tab: str = '🔔 '
|
||||
bell_path: str | None = None
|
||||
|
|
|
|||
|
|
@ -177,6 +177,11 @@ def simple_parse(func: str, rest: str) -> FuncArgsType:
|
|||
return func, (rest,)
|
||||
|
||||
|
||||
@func_with_args('change_background_image')
|
||||
def parse_change_background_image(func: str, rest: str) -> FuncArgsType:
|
||||
return func, (rest.strip() if rest.strip() else '+1',)
|
||||
|
||||
|
||||
@func_with_args('set_font_size')
|
||||
def float_parse(func: str, rest: str) -> FuncArgsType:
|
||||
return func, (float(rest),)
|
||||
|
|
|
|||
|
|
@ -79,8 +79,9 @@ failed, the command will exit with a success code.
|
|||
|
||||
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
||||
if len(args) != 1:
|
||||
self.fatal('Must specify path to exactly one PNG image')
|
||||
self.fatal('Must specify path to exactly one PNG image, or an index (+N, -N, N)')
|
||||
path = os.path.expanduser(args[0])
|
||||
import re
|
||||
import secrets
|
||||
ret = {
|
||||
'match': opts.match,
|
||||
|
|
@ -89,6 +90,10 @@ failed, the command will exit with a success code.
|
|||
'all': opts.all,
|
||||
'stream_id': secrets.token_urlsafe(),
|
||||
}
|
||||
# Handle index arguments (+N, -N, or N)
|
||||
if re.match(r'^[+-]?\d+$', path):
|
||||
ret['data'] = f'index:{path}'
|
||||
return ret
|
||||
if path.lower() == 'none':
|
||||
ret['data'] = '-'
|
||||
return ret
|
||||
|
|
@ -112,6 +117,14 @@ failed, the command will exit with a success code.
|
|||
windows = self.windows_for_payload(boss, window, payload_get, window_match_name='match')
|
||||
os_windows = tuple({w.os_window_id for w in windows if w})
|
||||
layout = payload_get('layout')
|
||||
if isinstance(data, str) and data.startswith('index:'):
|
||||
from kitty.fast_data_types import change_bg_image
|
||||
index_str = data[6:]
|
||||
is_delta = index_str[0] in '+-'
|
||||
value = int(index_str)
|
||||
for osw_id in os_windows:
|
||||
change_bg_image(osw_id, value, is_delta)
|
||||
return None
|
||||
if data == '-':
|
||||
path = None
|
||||
tfile = BytesIO()
|
||||
|
|
|
|||
143
kitty/state.c
143
kitty/state.c
|
|
@ -1314,6 +1314,8 @@ PYWRAP1(update_tab_bar_edge_colors) {
|
|||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
static uint32_t bgimage_id_counter = 0;
|
||||
|
||||
static PyObject*
|
||||
pyset_background_image(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
||||
const char *path;
|
||||
|
|
@ -1340,7 +1342,6 @@ pyset_background_image(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
|||
free(bgimage);
|
||||
return NULL;
|
||||
}
|
||||
static uint32_t bgimage_id_counter = 0;
|
||||
bgimage->id = ++bgimage_id_counter;
|
||||
send_bgimage_to_gpu(layout, bgimage);
|
||||
bgimage->refcnt++;
|
||||
|
|
@ -1368,6 +1369,143 @@ pyset_background_image(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
free_bg_image_list(bool release_textures) {
|
||||
BackgroundImageList *list = &global_state.bg_image_list;
|
||||
for (unsigned i = 0; i < list->count; i++) {
|
||||
if (list->paths[i]) free(list->paths[i]);
|
||||
if (list->images[i]) {
|
||||
list->images[i]->refcnt = 1;
|
||||
free_bgimage(&list->images[i], release_textures);
|
||||
}
|
||||
}
|
||||
free(list->paths); list->paths = NULL;
|
||||
free(list->images); list->images = NULL;
|
||||
list->count = 0;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
pyset_bg_image_paths(PyObject *self UNUSED, PyObject *args) {
|
||||
PyObject *paths_list;
|
||||
if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &paths_list)) return NULL;
|
||||
|
||||
free_bg_image_list(true);
|
||||
|
||||
Py_ssize_t count = PyList_GET_SIZE(paths_list);
|
||||
if (count == 0) Py_RETURN_NONE;
|
||||
|
||||
BackgroundImageList *list = &global_state.bg_image_list;
|
||||
list->paths = calloc(count, sizeof(char*));
|
||||
list->images = calloc(count, sizeof(BackgroundImage*));
|
||||
if (!list->paths || !list->images) return PyErr_NoMemory();
|
||||
|
||||
for (Py_ssize_t i = 0; i < count; i++) {
|
||||
const char *path = PyUnicode_AsUTF8(PyList_GET_ITEM(paths_list, i));
|
||||
if (!path) continue;
|
||||
list->paths[i] = strdup(path);
|
||||
list->images[i] = NULL; // lazy loaded
|
||||
}
|
||||
list->count = (unsigned int)count;
|
||||
|
||||
// Sync images[0] with the already-loaded global bgimage
|
||||
if (global_state.bgimage && global_state.bgimage->texture_id) {
|
||||
list->images[0] = global_state.bgimage;
|
||||
global_state.bgimage->refcnt++;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static BackgroundImage*
|
||||
load_bg_image_at_index(unsigned int idx) {
|
||||
BackgroundImageList *list = &global_state.bg_image_list;
|
||||
if (idx >= list->count) return NULL;
|
||||
|
||||
BackgroundImage *bgimage = calloc(1, sizeof(BackgroundImage));
|
||||
if (!bgimage) return NULL;
|
||||
|
||||
if (!image_path_to_bitmap(list->paths[idx], &bgimage->bitmap, &bgimage->width, &bgimage->height, &bgimage->mmap_size)) {
|
||||
free(bgimage);
|
||||
// Remove failed entry from list
|
||||
free(list->paths[idx]);
|
||||
for (unsigned i = idx; i < list->count - 1; i++) {
|
||||
list->paths[i] = list->paths[i + 1];
|
||||
list->images[i] = list->images[i + 1];
|
||||
}
|
||||
list->count--;
|
||||
return NULL; // caller should retry with same index
|
||||
}
|
||||
|
||||
bgimage->id = ++bgimage_id_counter;
|
||||
send_bgimage_to_gpu(OPT(background_image_layout), bgimage);
|
||||
bgimage->refcnt = 1; // owned by list
|
||||
list->images[idx] = bgimage;
|
||||
return bgimage;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
pychange_bg_image(PyObject *self UNUSED, PyObject *args) {
|
||||
id_type os_window_id;
|
||||
int value;
|
||||
int is_delta;
|
||||
if (!PyArg_ParseTuple(args, "Kip", &os_window_id, &value, &is_delta)) return NULL;
|
||||
|
||||
BackgroundImageList *list = &global_state.bg_image_list;
|
||||
if (list->count == 0) Py_RETURN_FALSE;
|
||||
|
||||
bool success = false;
|
||||
WITH_OS_WINDOW(os_window_id)
|
||||
make_os_window_context_current(os_window);
|
||||
|
||||
int new_idx;
|
||||
if (is_delta) {
|
||||
int count = (int)list->count;
|
||||
new_idx = ((os_window->bg_image_idx + value) % count + count) % count;
|
||||
} else {
|
||||
new_idx = value;
|
||||
if (new_idx < 0) new_idx = 0;
|
||||
if (new_idx >= (int)list->count) new_idx = (int)list->count - 1;
|
||||
}
|
||||
|
||||
// Lazy load with retry on failure
|
||||
int attempts = 0;
|
||||
while (list->count > 0 && attempts < (int)list->count) {
|
||||
if (new_idx >= (int)list->count) new_idx = 0;
|
||||
if (list->images[new_idx]) break;
|
||||
if (load_bg_image_at_index((unsigned int)new_idx)) break;
|
||||
attempts++;
|
||||
}
|
||||
|
||||
if (list->count > 0 && new_idx < (int)list->count && list->images[new_idx]) {
|
||||
os_window->bg_image_idx = new_idx;
|
||||
|
||||
// Release old bgimage
|
||||
if (os_window->bgimage) {
|
||||
os_window->bgimage->refcnt--;
|
||||
if (os_window->bgimage->refcnt == 0) {
|
||||
bool in_list = false;
|
||||
for (unsigned i = 0; i < list->count; i++) {
|
||||
if (list->images[i] == os_window->bgimage) { in_list = true; break; }
|
||||
}
|
||||
if (!in_list) {
|
||||
free_bgimage_bitmap(os_window->bgimage);
|
||||
free_texture(&os_window->bgimage->texture_id);
|
||||
free(os_window->bgimage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
os_window->bgimage = list->images[new_idx];
|
||||
os_window->bgimage->refcnt++;
|
||||
os_window->render_calls = 0;
|
||||
success = true;
|
||||
}
|
||||
END_WITH_OS_WINDOW
|
||||
|
||||
if (success) Py_RETURN_TRUE;
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
PYWRAP0(destroy_global_data) {
|
||||
Py_CLEAR(global_state.boss);
|
||||
free(global_state.os_windows); global_state.os_windows = NULL;
|
||||
|
|
@ -1676,6 +1814,8 @@ static PyMethodDef module_methods[] = {
|
|||
MW(set_os_window_pos, METH_VARARGS),
|
||||
MW(global_font_size, METH_VARARGS),
|
||||
{"set_background_image", (PyCFunction)(void (*) (void))pyset_background_image, METH_VARARGS | METH_KEYWORDS, ""},
|
||||
{"set_bg_image_paths", (PyCFunction)pyset_bg_image_paths, METH_VARARGS, ""},
|
||||
{"change_bg_image", (PyCFunction)pychange_bg_image, METH_VARARGS, ""},
|
||||
MW(os_window_font_size, METH_VARARGS),
|
||||
MW(set_os_window_size, METH_VARARGS),
|
||||
MW(get_os_window_size, METH_VARARGS),
|
||||
|
|
@ -1710,6 +1850,7 @@ finalize(void) {
|
|||
// the GPU driver should take care of it when the OpenGL context is
|
||||
// destroyed.
|
||||
free_bgimage(&global_state.bgimage, false);
|
||||
free_bg_image_list(false);
|
||||
free_window_logo_table(&global_state.all_window_logos);
|
||||
global_state.bgimage = NULL;
|
||||
free_drag_source();
|
||||
|
|
|
|||
|
|
@ -394,6 +394,7 @@ typedef struct OSWindow {
|
|||
double viewport_x_ratio, viewport_y_ratio;
|
||||
Tab *tabs;
|
||||
BackgroundImage *bgimage;
|
||||
int bg_image_idx;
|
||||
struct {
|
||||
uint32_t framebuffer_id, attached_texture_generation;
|
||||
} indirect_output;
|
||||
|
|
@ -445,6 +446,7 @@ typedef struct GlobalState {
|
|||
id_type os_window_id_counter, tab_id_counter, window_id_counter;
|
||||
PyObject *boss;
|
||||
BackgroundImage *bgimage;
|
||||
BackgroundImageList bg_image_list;
|
||||
OSWindow *os_windows;
|
||||
size_t num_os_windows, capacity;
|
||||
OSWindow *callback_os_window;
|
||||
|
|
|
|||
167
tools/cmd/at/cmd_set_background_image_generated.go
generated
Normal file
167
tools/cmd/at/cmd_set_background_image_generated.go
generated
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
// Code generated by go_code.py; DO NOT EDIT.
|
||||
|
||||
|
||||
|
||||
|
||||
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||
|
||||
// Code generated by gen-go-code.py; DO NOT EDIT.
|
||||
|
||||
package at
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kovidgoyal/kitty/tools/cli"
|
||||
"github.com/kovidgoyal/kitty/tools/utils"
|
||||
)
|
||||
|
||||
var _ = fmt.Print
|
||||
var _ = strings.Join
|
||||
|
||||
type options_set_background_image_type struct {
|
||||
All bool
|
||||
Configured bool
|
||||
Layout string
|
||||
Match string
|
||||
}
|
||||
|
||||
var options_set_background_image options_set_background_image_type
|
||||
|
||||
type set_background_image_json_type struct {
|
||||
Data escaped_string`json:"data,omitempty"`
|
||||
Match escaped_string`json:"match,omitempty"`
|
||||
Layout string`json:"layout,omitempty"`
|
||||
All bool`json:"all,omitempty"`
|
||||
Configured bool`json:"configured,omitempty"`
|
||||
}
|
||||
|
||||
func create_payload_set_background_image(io_data *rc_io_data, cmd *cli.Command, args []string) (err error) {
|
||||
payload := set_background_image_json_type{}
|
||||
if len(args) != 1 { return fmt.Errorf("%s", "Must specify exactly 1 argument(s) for set_background_image") }
|
||||
io_data.multiple_payload_generator, err = read_window_logo(io_data, args[0])
|
||||
if err != nil { return err }
|
||||
payload.Match = escaped_string(options_set_background_image.Match)
|
||||
payload.Layout = options_set_background_image.Layout
|
||||
payload.All = options_set_background_image.All
|
||||
payload.Configured = options_set_background_image.Configured
|
||||
io_data.rc.Payload = payload
|
||||
return
|
||||
}
|
||||
|
||||
func create_rc_set_background_image(args []string) (*utils.RemoteControlCmd, error) {
|
||||
rc := utils.RemoteControlCmd{
|
||||
Cmd: "set-background-image",
|
||||
Version: ProtocolVersion,
|
||||
NoResponse: false,
|
||||
Stream: true,
|
||||
}
|
||||
if rc.Stream {
|
||||
stream_id, err := utils.HumanRandomId(128)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc.StreamId = stream_id
|
||||
}
|
||||
if false {
|
||||
async_id, err := utils.HumanRandomId(128)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc.Async = async_id
|
||||
}
|
||||
return &rc, nil
|
||||
}
|
||||
|
||||
func run_set_background_image(cmd *cli.Command, args []string) (return_code int, err error) {
|
||||
err = cmd.GetOptionValues(&options_set_background_image)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rc, err := create_rc_set_background_image(args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
nrv, err := cli.GetOptionValue[bool](cmd, "NoResponse")
|
||||
if err == nil {
|
||||
rc.NoResponse = nrv
|
||||
}
|
||||
var timeout float64 = 10.0
|
||||
rt, err := cli.GetOptionValue[float64](cmd, "ResponseTimeout")
|
||||
if err == nil {
|
||||
timeout = rt
|
||||
}
|
||||
io_data := rc_io_data{
|
||||
cmd: cmd,
|
||||
rc: rc,
|
||||
timeout: time.Duration(timeout * float64(time.Second)),
|
||||
string_response_is_err: false,
|
||||
}
|
||||
err = create_payload_set_background_image(&io_data, cmd, args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = send_rc_command(&io_data)
|
||||
if ee, ok := err.(*exit_error); ok && !running_shell {
|
||||
return ee.exit_code, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setup_set_background_image(parent *cli.Command) *cli.Command {
|
||||
ans := parent.AddSubCommand(&cli.Command{
|
||||
Name: "set-background-image",
|
||||
Usage: " PATH_TO_PNG_IMAGE",
|
||||
ShortDescription: "Set the background image",
|
||||
HelpText: "Set the background image for the specified OS windows. You must specify the path to an image that will be used as the background. If you specify the special value :code:`none` then any existing image will be removed. Supported image formats are: PNG, JPEG, WEBP, GIF, BMP, TIFF",
|
||||
Run: run_set_background_image,
|
||||
})
|
||||
ans.StopCompletingAtArg = 1
|
||||
ans.ArgCompleter = cli.FnmatchCompleter("Images", cli.CWD, "*.png", "*.jpg", "*.jpeg", "*.webp", "*.gif", "*.bmp", "*.tiff")
|
||||
ans.Add(cli.OptionSpec{
|
||||
Name: "--all -a",
|
||||
Type: "bool-set",
|
||||
Dest: "All",
|
||||
Help: "By default, background image is only changed for the currently active OS window. This option will cause the image to be changed in all windows.",
|
||||
})
|
||||
ans.Add(cli.OptionSpec{
|
||||
Name: "--configured -c",
|
||||
Type: "bool-set",
|
||||
Dest: "Configured",
|
||||
Help: "Change the configured background image which is used for new OS windows.",
|
||||
})
|
||||
ans.Add(cli.OptionSpec{
|
||||
Name: "--layout",
|
||||
Type: "choices",
|
||||
Dest: "Layout",
|
||||
Help: "How the image should be displayed. A value of :code:`configured` will use the configured value.",
|
||||
|
||||
Choices: "configured, centered, clamped, cscaled, mirror-tiled, scaled, tiled",
|
||||
|
||||
Completer: cli.NamesCompleter("Choices for layout", "configured", "centered", "clamped", "cscaled", "mirror-tiled", "scaled", "tiled"),
|
||||
Default: "configured",
|
||||
})
|
||||
ans.Add(cli.OptionSpec{
|
||||
Name: "--no-response",
|
||||
Type: "bool-set",
|
||||
Dest: "NoResponse",
|
||||
Help: "Don't wait for a response from kitty. This means that even if setting the background image failed, the command will exit with a success code.",
|
||||
|
||||
Default: "false",
|
||||
})
|
||||
ans.Add(cli.OptionSpec{
|
||||
Name: "--match -m",
|
||||
Type: "",
|
||||
Dest: "Match",
|
||||
Help: "The window to match. Match specifications are of the form: :italic:`field:query`. Where :italic:`field` can be one of: :code:`id`, :code:`title`, :code:`pid`, :code:`cwd`, :code:`cmdline`, :code:`num`, :code:`env`, :code:`var`, :code:`state`, :code:`neighbor`, :code:`session` and :code:`recent`. :italic:`query` is the expression to match. Expressions can be either a number or a regular expression, and can be :ref:`combined using Boolean operators <search_syntax>`.\n\nThe special value :code:`all` matches all windows.\n\nFor numeric fields: :code:`id`, :code:`pid`, :code:`num` and :code:`recent`, the expression is interpreted as a number, not a regular expression. Negative values for :code:`id` match from the highest id number down, in particular, -1 is the most recently created window.\n\nThe field :code:`num` refers to the window position in the current tab, starting from zero and counting clockwise (this is the same as the order in which the windows are reported by the :ref:`kitten @ ls <at-ls>` command).\n\nThe window id of the current window is available as the :envvar:`KITTY_WINDOW_ID` environment variable.\n\nThe field :code:`recent` refers to recently active windows in the currently active tab, with zero being the currently active window, one being the previously active window and so on.\n\nThe field :code:`neighbor` refers to a neighbor of the active window in the specified direction, which can be: :code:`left`, :code:`right`, :code:`top` or :code:`bottom`.\n\nThe field :code:`session` matches windows that were created in the specified session. Use the expression :code:`^$` to match windows that were not created in a session and :code:`.` to match the currently active session and :code:`~` to match either the currently active session or the last active session when no session is active.\n\nWhen using the :code:`env` field to match on environment variables, you can specify only the environment variable name or a name and value, for example, :code:`env:MY_ENV_VAR=2`.\n\nSimilarly, the :code:`var` field matches on user variables set on the window. You can specify name or name and value as with the :code:`env` field.\n\nThe field :code:`state` matches on the state of the window. Supported states are: :code:`active`, :code:`focused`, :code:`needs_attention`, :code:`parent_active`, :code:`parent_focused`, :code:`focused_os_window`, :code:`self`, :code:`overlay_parent`. Active windows are the windows that are active in their parent tab. There is only one focused window and it is the window to which keyboard events are delivered. If no window is focused, the last focused window is matched. The value :code:`focused_os_window` matches all windows in the currently focused OS window. The value :code:`self` matches the window in which the remote control command is run. The value :code:`overlay_parent` matches the window that is under the :code:`self` window, when the self window is an overlay.\n\nNote that you can use the :ref:`kitten @ ls <at-ls>` command to get a list of windows.",
|
||||
})
|
||||
return ans
|
||||
}
|
||||
|
||||
func init() {
|
||||
register_at_cmd(setup_set_background_image)
|
||||
}
|
||||
|
|
@ -18,6 +18,25 @@ func set_payload_data(io_data *rc_io_data, data string) {
|
|||
set_payload_string_field(io_data, "Data", data)
|
||||
}
|
||||
|
||||
func isIndexArg(s string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
start := 0
|
||||
if s[0] == '+' || s[0] == '-' {
|
||||
start = 1
|
||||
}
|
||||
if start >= len(s) {
|
||||
return false
|
||||
}
|
||||
for _, c := range s[start:] {
|
||||
if c < '0' || c > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func read_window_logo(io_data *rc_io_data, path string) (func(io_data *rc_io_data) (bool, error), error) {
|
||||
if strings.ToLower(path) == "none" {
|
||||
io_data.rc.Stream = false
|
||||
|
|
@ -27,6 +46,14 @@ func read_window_logo(io_data *rc_io_data, path string) (func(io_data *rc_io_dat
|
|||
}, nil
|
||||
}
|
||||
|
||||
if isIndexArg(path) {
|
||||
io_data.rc.Stream = false
|
||||
return func(io_data *rc_io_data) (bool, error) {
|
||||
set_payload_data(io_data, "index:"+path)
|
||||
return true, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue