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/cobra v1.5.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
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
|
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=
|
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 h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
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 h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
|||||||
@ -6,7 +6,7 @@ from contextlib import suppress
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING, Any, Callable, Dict, FrozenSet, Iterable, Iterator,
|
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
|
from kitty.cli import get_defaults_from_seq, parse_args, parse_option_spec
|
||||||
@ -88,6 +88,7 @@ CmdGenerator = Iterator[CmdReturnType]
|
|||||||
PayloadType = Optional[Union[CmdReturnType, CmdGenerator]]
|
PayloadType = Optional[Union[CmdReturnType, CmdGenerator]]
|
||||||
PayloadGetType = PayloadGetter
|
PayloadGetType = PayloadGetter
|
||||||
ArgsType = List[str]
|
ArgsType = List[str]
|
||||||
|
ImageCompletion: Dict[str, Tuple[str, Tuple[str, ...]]] = {'files': ('Images', ('*.png', '*.jpg', '*.jpeg', '*.webp', '*.gif', '*.bmp', '*.tiff'))}
|
||||||
|
|
||||||
|
|
||||||
MATCH_WINDOW_OPTION = '''\
|
MATCH_WINDOW_OPTION = '''\
|
||||||
@ -183,7 +184,7 @@ class ArgsHandling:
|
|||||||
json_field: str = ''
|
json_field: str = ''
|
||||||
count: Optional[int] = None
|
count: Optional[int] = None
|
||||||
spec: str = ''
|
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, ...] = ()
|
value_if_unspecified: Tuple[str, ...] = ()
|
||||||
minimum_count: int = -1
|
minimum_count: int = -1
|
||||||
first_rest: Optional[Tuple[str, str]] = None
|
first_rest: Optional[Tuple[str, str]] = None
|
||||||
|
|||||||
@ -9,8 +9,9 @@ from typing import TYPE_CHECKING, Optional
|
|||||||
from kitty.types import AsyncResponse
|
from kitty.types import AsyncResponse
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
MATCH_WINDOW_OPTION, ArgsType, Boss, CmdGenerator, NamedTemporaryFile,
|
MATCH_WINDOW_OPTION, ArgsType, Boss, CmdGenerator, ImageCompletion,
|
||||||
PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window
|
NamedTemporaryFile, PayloadGetType, PayloadType, RCOptions, RemoteCommand,
|
||||||
|
ResponseType, Window
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -62,8 +63,8 @@ default=false
|
|||||||
Don't wait for a response from kitty. This means that even if setting the background image
|
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.
|
failed, the command will exit with a success code.
|
||||||
''' + '\n\n' + MATCH_WINDOW_OPTION
|
''' + '\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={
|
args = RemoteCommand.Args(spec='PATH_TO_PNG_IMAGE', count=1, json_field='data', special_parse='!read_window_logo(args[0])',
|
||||||
'files': ('PNG Images', ('*.png',))})
|
completion=ImageCompletion)
|
||||||
reads_streaming_data = True
|
reads_streaming_data = True
|
||||||
|
|
||||||
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
||||||
|
|||||||
@ -10,8 +10,9 @@ from typing import TYPE_CHECKING, Optional
|
|||||||
from kitty.types import AsyncResponse
|
from kitty.types import AsyncResponse
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
MATCH_WINDOW_OPTION, ArgsType, Boss, CmdGenerator, NamedTemporaryFile,
|
MATCH_WINDOW_OPTION, ArgsType, Boss, CmdGenerator, ImageCompletion,
|
||||||
PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType, Window
|
NamedTemporaryFile, PayloadGetType, PayloadType, RCOptions, RemoteCommand,
|
||||||
|
ResponseType, Window
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -58,8 +59,7 @@ default=false
|
|||||||
Don't wait for a response from kitty. This means that even if setting the image
|
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.
|
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={
|
args = RemoteCommand.Args(spec='PATH_TO_PNG_IMAGE', count=1, json_field='data', special_parse='!read_window_logo(args[0])', completion=ImageCompletion)
|
||||||
'files': ('PNG Images', ('*.png',))})
|
|
||||||
reads_streaming_data = True
|
reads_streaming_data = True
|
||||||
|
|
||||||
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType:
|
||||||
|
|||||||
@ -3,12 +3,20 @@
|
|||||||
package at
|
package at
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"image"
|
||||||
|
_ "image/gif"
|
||||||
|
_ "image/jpeg"
|
||||||
|
"image/png"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
_ "golang.org/x/image/bmp"
|
||||||
|
_ "golang.org/x/image/tiff"
|
||||||
|
_ "golang.org/x/image/webp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type struct_with_data interface {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
buf := make([]byte, 2048)
|
var image_data_stream io.Reader
|
||||||
n, err := f.Read(buf)
|
image_data_stream = f
|
||||||
if err != nil && err != io.EOF {
|
config, format, ierr := image.DecodeConfig(f)
|
||||||
f.Close()
|
if ierr != nil {
|
||||||
return nil, err
|
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()
|
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
|
is_first_call := true
|
||||||
|
buf := make([]byte, 2048)
|
||||||
|
|
||||||
return func(io_data *rc_io_data) (bool, error) {
|
return func(io_data *rc_io_data) (bool, error) {
|
||||||
if is_first_call {
|
if is_first_call {
|
||||||
@ -51,18 +71,16 @@ func read_window_logo(path string) (func(io_data *rc_io_data) (bool, error), err
|
|||||||
} else {
|
} else {
|
||||||
io_data.rc.Stream = false
|
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)]
|
buf = buf[:cap(buf)]
|
||||||
n, err := f.Read(buf)
|
n, err := image_data_stream.Read(buf)
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
buf = buf[:n]
|
buf = buf[:n]
|
||||||
|
set_payload_data(io_data, base64.StdEncoding.EncodeToString(buf))
|
||||||
|
if err == io.EOF {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user