mirror of
https://github.com/kovidgoyal/kitty.git
synced 2024-09-20 02:57:19 +03:00
Allow using many image formats in RC commands
This commit is contained in:
parent
fa7a6dfd4a
commit
905c4d641c
1
go.mod
1
go.mod
@ -11,6 +11,7 @@ require (
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
|
||||
)
|
||||
|
||||
|
4
go.sum
4
go.sum
@ -20,7 +20,11 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU=
|
||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
@ -6,7 +6,7 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import (
|
||||
TYPE_CHECKING, Any, Callable, Dict, FrozenSet, Iterable, Iterator,
|
||||
List, NoReturn, Optional, Set, Tuple, Type, Union, cast
|
||||
List, NoReturn, Optional, Set, Tuple, Type, Union, cast, Mapping
|
||||
)
|
||||
|
||||
from kitty.cli import get_defaults_from_seq, parse_args, parse_option_spec
|
||||
@ -88,6 +88,7 @@ def __call__(self, key: str, opt_name: Optional[str] = None, missing: Any = None
|
||||
PayloadType = Optional[Union[CmdReturnType, CmdGenerator]]
|
||||
PayloadGetType = PayloadGetter
|
||||
ArgsType = List[str]
|
||||
ImageCompletion: Dict[str, Tuple[str, Tuple[str, ...]]] = {'files': ('Images', ('*.png', '*.jpg', '*.jpeg', '*.webp', '*.gif', '*.bmp', '*.tiff'))}
|
||||
|
||||
|
||||
MATCH_WINDOW_OPTION = '''\
|
||||
@ -183,7 +184,7 @@ class ArgsHandling:
|
||||
json_field: str = ''
|
||||
count: Optional[int] = None
|
||||
spec: str = ''
|
||||
completion: Optional[Dict[str, Tuple[str, Union[Callable[[], Iterable[str]], Tuple[str, ...]]]]] = None
|
||||
completion: Optional[Mapping[str, Tuple[str, Union[Callable[[], Iterable[str]], Tuple[str, ...]]]]] = None
|
||||
value_if_unspecified: Tuple[str, ...] = ()
|
||||
minimum_count: int = -1
|
||||
first_rest: Optional[Tuple[str, str]] = None
|
||||
|
@ -9,8 +9,9 @@
|
||||
from kitty.types import AsyncResponse
|
||||
|
||||
from .base import (
|
||||
MATCH_WINDOW_OPTION, ArgsType, Boss, CmdGenerator, NamedTemporaryFile,
|
||||
PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window
|
||||
MATCH_WINDOW_OPTION, ArgsType, Boss, CmdGenerator, ImageCompletion,
|
||||
NamedTemporaryFile, PayloadGetType, PayloadType, RCOptions, RemoteCommand,
|
||||
ResponseType, Window
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -62,8 +63,8 @@ class SetBackgroundImage(RemoteCommand):
|
||||
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.
|
||||
''' + '\n\n' + MATCH_WINDOW_OPTION
|
||||
args = RemoteCommand.Args(spec='PATH_TO_PNG_IMAGE', count=1, json_field='data', special_parse='!read_window_logo(args[0])', completion={
|
||||
'files': ('PNG Images', ('*.png',))})
|
||||
args = RemoteCommand.Args(spec='PATH_TO_PNG_IMAGE', count=1, json_field='data', special_parse='!read_window_logo(args[0])',
|
||||
completion=ImageCompletion)
|
||||
reads_streaming_data = True
|
||||
|
||||
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
||||
|
@ -10,8 +10,9 @@
|
||||
from kitty.types import AsyncResponse
|
||||
|
||||
from .base import (
|
||||
MATCH_WINDOW_OPTION, ArgsType, Boss, CmdGenerator, NamedTemporaryFile,
|
||||
PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window
|
||||
MATCH_WINDOW_OPTION, ArgsType, Boss, CmdGenerator, ImageCompletion,
|
||||
NamedTemporaryFile, PayloadGetType, PayloadType, RCOptions, RemoteCommand,
|
||||
ResponseType, Window
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -58,8 +59,7 @@ class SetWindowLogo(RemoteCommand):
|
||||
Don't wait for a response from kitty. This means that even if setting the image
|
||||
failed, the command will exit with a success code.
|
||||
'''
|
||||
args = RemoteCommand.Args(spec='PATH_TO_PNG_IMAGE', count=1, json_field='data', special_parse='!read_window_logo(args[0])', completion={
|
||||
'files': ('PNG Images', ('*.png',))})
|
||||
args = RemoteCommand.Args(spec='PATH_TO_PNG_IMAGE', count=1, json_field='data', special_parse='!read_window_logo(args[0])', completion=ImageCompletion)
|
||||
reads_streaming_data = True
|
||||
|
||||
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
||||
|
@ -3,12 +3,20 @@
|
||||
package at
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"image"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
_ "golang.org/x/image/bmp"
|
||||
_ "golang.org/x/image/tiff"
|
||||
_ "golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
type struct_with_data interface {
|
||||
@ -31,19 +39,31 @@ func read_window_logo(path string) (func(io_data *rc_io_data) (bool, error), err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := make([]byte, 2048)
|
||||
n, err := f.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
f.Close()
|
||||
return nil, err
|
||||
var image_data_stream io.Reader
|
||||
image_data_stream = f
|
||||
config, format, ierr := image.DecodeConfig(f)
|
||||
if ierr != nil {
|
||||
return nil, fmt.Errorf("%s is not a supported image format", path)
|
||||
}
|
||||
buf = buf[:n]
|
||||
f.Seek(0, 0)
|
||||
|
||||
if http.DetectContentType(buf) != "image/png" {
|
||||
if format != "png" {
|
||||
f.Seek(0, 0)
|
||||
img, _, err := image.Decode(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
}
|
||||
f.Close()
|
||||
return nil, fmt.Errorf("%s is not a PNG image", path)
|
||||
b := bytes.Buffer{}
|
||||
b.Grow(config.Height * config.Width * 4)
|
||||
err = png.Encode(&b, img)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
image_data_stream = &b
|
||||
}
|
||||
is_first_call := true
|
||||
buf := make([]byte, 2048)
|
||||
|
||||
return func(io_data *rc_io_data) (bool, error) {
|
||||
if is_first_call {
|
||||
@ -51,18 +71,16 @@ func read_window_logo(path string) (func(io_data *rc_io_data) (bool, error), err
|
||||
} else {
|
||||
io_data.rc.Stream = false
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
set_payload_data(io_data, "")
|
||||
io_data.rc.Stream = false
|
||||
return true, nil
|
||||
}
|
||||
set_payload_data(io_data, base64.StdEncoding.EncodeToString(buf))
|
||||
buf = buf[:cap(buf)]
|
||||
n, err := f.Read(buf)
|
||||
n, err := image_data_stream.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
return false, err
|
||||
}
|
||||
buf = buf[:n]
|
||||
set_payload_data(io_data, base64.StdEncoding.EncodeToString(buf))
|
||||
if err == io.EOF {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user