fix(graphics): answer kitten icat transport probes
- commit
- 6ace06390b45186b7eab4982fb253e79c6c018ff
- author
- brice · · 2026-08-26 09:40 UTC
- parent
- 0b9cfda
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +24 -3
M protocol/kittygraphics/controls.go +6 -3
| @@ -127,10 +127,13 @@ func parseControl(c *Controls, key byte, value []byte) error { | |||
| 127 | 127 | return fmt.Errorf("%w: d=%q", ErrInvalidCommand, value) | |
| 128 | 128 | } | |
| 129 | 129 | case ControlTransmission: | |
| 130 | - | if len(value) != 1 || Transmission(value[0]) != TransmissionDirect { | |
| 131 | - | return fmt.Errorf("%w: t=%q", ErrUnsupported, value) | |
| 130 | + | if len(value) != 1 { | |
| 131 | + | return fmt.Errorf("%w: t=%q", ErrInvalidCommand, value) | |
| 132 | 132 | } | |
| 133 | - | c.Transmission, c.HasTransmission = TransmissionDirect, true | |
| 133 | + | // Preserve unsupported transports long enough for Session to emit the | |
| 134 | + | // protocol ENOTSUP response. Rejecting them during header parsing leaves | |
| 135 | + | // capability probes such as kitten icat waiting until timeout. | |
| 136 | + | c.Transmission, c.HasTransmission = Transmission(value[0]), true | |
| 134 | 137 | case ControlCursor: | |
| 135 | 138 | return setUint(&c.Cursor, &c.HasCursor, key, value) | |
| 136 | 139 | case ControlSourceX: | |
| @@ -127,10 +127,13 @@ func parseControl(c *Controls, key byte, value []byte) error { | |||||
| 127 | return fmt.Errorf("%w: d=%q", ErrInvalidCommand, value) | 127 | return fmt.Errorf("%w: d=%q", ErrInvalidCommand, value) | ||
| 128 | } | 128 | } | ||
| 129 | case ControlTransmission: | 129 | case ControlTransmission: | ||
| 130 | - | if len(value) != 1 || Transmission(value[0]) != TransmissionDirect { | 130 | + | if len(value) != 1 { |
| 131 | - | return fmt.Errorf("%w: t=%q", ErrUnsupported, value) | 131 | + | return fmt.Errorf("%w: t=%q", ErrInvalidCommand, value) |
| 132 | } | 132 | } | ||
| 133 | - | c.Transmission, c.HasTransmission = TransmissionDirect, true | 133 | + | // Preserve unsupported transports long enough for Session to emit the |
| 134 | + | // protocol ENOTSUP response. Rejecting them during header parsing leaves | |||
| 135 | + | // capability probes such as kitten icat waiting until timeout. | |||
| 136 | + | c.Transmission, c.HasTransmission = Transmission(value[0]), true | |||
| 134 | case ControlCursor: | 137 | case ControlCursor: | ||
| 135 | return setUint(&c.Cursor, &c.HasCursor, key, value) | 138 | return setUint(&c.Cursor, &c.HasCursor, key, value) | ||
| 136 | case ControlSourceX: | 139 | case ControlSourceX: | ||
M screen_graphics_test.go +18 -0
| @@ -42,6 +42,24 @@ func TestScreenKittyGraphicsFragmentedAPCAndResponse(t *testing.T) { | |||
| 42 | 42 | require.Equal(t, uint64(1), snapshot.Usage().Placements) | |
| 43 | 43 | } | |
| 44 | 44 | ||
| 45 | + | func TestScreenKittyIcatDetectionResponses(t *testing.T) { | |
| 46 | + | screen := NewScreen(80, 24) | |
| 47 | + | var responses []string | |
| 48 | + | screen.OnResponse = func(response []byte) { responses = append(responses, string(response)) } | |
| 49 | + | ||
| 50 | + | screen.Write([]byte("\x1b_Ga=q,f=24,s=1,v=1,S=3,i=1;MTIz\x1b\\")) | |
| 51 | + | screen.Write([]byte("\x1b_Ga=q,f=24,t=t,s=1,v=1,S=47,i=2;L2Rldi9zaG0va2l0dHktdHR5LWdyYXBoaWNzLXByb3RvY29sLTMzMTU3NTkxNjc\x1b\\")) | |
| 52 | + | screen.Write([]byte("\x1b_Ga=q,f=24,t=s,s=1,v=1,S=18,i=3;aWNhdC1aQlJCWFdNQ0lIQ0ZD\x1b\\")) | |
| 53 | + | screen.Write([]byte("\x1b[c")) | |
| 54 | + | ||
| 55 | + | require.Equal(t, []string{ | |
| 56 | + | "\x1b_Gi=1;OK\x1b\\", | |
| 57 | + | "\x1b_Gi=2;ENOTSUP\x1b\\", | |
| 58 | + | "\x1b_Gi=3;ENOTSUP\x1b\\", | |
| 59 | + | "\x1b[?6c", | |
| 60 | + | }, responses) | |
| 61 | + | } | |
| 62 | + | ||
| 45 | 63 | func TestScreenKittyGraphicsPrimaryAndAlternateState(t *testing.T) { | |
| 46 | 64 | screen := NewScreen(8, 3) | |
| 47 | 65 | screen.Write(screenKittyImageAPC("T")) | |
| @@ -42,6 +42,24 @@ func TestScreenKittyGraphicsFragmentedAPCAndResponse(t *testing.T) { | |||||
| 42 | require.Equal(t, uint64(1), snapshot.Usage().Placements) | 42 | require.Equal(t, uint64(1), snapshot.Usage().Placements) | ||
| 43 | } | 43 | } | ||
| 44 | 44 | ||||
| 45 | + | func TestScreenKittyIcatDetectionResponses(t *testing.T) { | |||
| 46 | + | screen := NewScreen(80, 24) | |||
| 47 | + | var responses []string | |||
| 48 | + | screen.OnResponse = func(response []byte) { responses = append(responses, string(response)) } | |||
| 49 | + | ||||
| 50 | + | screen.Write([]byte("\x1b_Ga=q,f=24,s=1,v=1,S=3,i=1;MTIz\x1b\\")) | |||
| 51 | + | screen.Write([]byte("\x1b_Ga=q,f=24,t=t,s=1,v=1,S=47,i=2;L2Rldi9zaG0va2l0dHktdHR5LWdyYXBoaWNzLXByb3RvY29sLTMzMTU3NTkxNjc\x1b\\")) | |||
| 52 | + | screen.Write([]byte("\x1b_Ga=q,f=24,t=s,s=1,v=1,S=18,i=3;aWNhdC1aQlJCWFdNQ0lIQ0ZD\x1b\\")) | |||
| 53 | + | screen.Write([]byte("\x1b[c")) | |||
| 54 | + | ||||
| 55 | + | require.Equal(t, []string{ | |||
| 56 | + | "\x1b_Gi=1;OK\x1b\\", | |||
| 57 | + | "\x1b_Gi=2;ENOTSUP\x1b\\", | |||
| 58 | + | "\x1b_Gi=3;ENOTSUP\x1b\\", | |||
| 59 | + | "\x1b[?6c", | |||
| 60 | + | }, responses) | |||
| 61 | + | } | |||
| 62 | + | ||||
| 45 | func TestScreenKittyGraphicsPrimaryAndAlternateState(t *testing.T) { | 63 | func TestScreenKittyGraphicsPrimaryAndAlternateState(t *testing.T) { | ||
| 46 | screen := NewScreen(8, 3) | 64 | screen := NewScreen(8, 3) | ||
| 47 | screen.Write(screenKittyImageAPC("T")) | 65 | screen.Write(screenKittyImageAPC("T")) | ||