Skip to content

Commit 0387de8

Browse files
fix: swap kitty keyboard flags on alt buffer switch (DECSET/DECRST 47/1047/1049)
When switching between main and alt screen buffers, the kitty keyboard flags and stacks are now properly swapped. Entering alt buffer saves main flags/stack and restores alt flags/stack. Leaving alt buffer does the reverse. Fixes #9 Co-authored-by: Ona <no-reply@ona.com>
1 parent 713c55b commit 0387de8

2 files changed

Lines changed: 145 additions & 0 deletions

File tree

inputhandler_csi.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,11 @@ func (h *InputHandler) setModePrivate(params *Params) bool {
642642
h.SaveCursor()
643643
fallthrough
644644
case 47, 1047:
645+
// Swap kitty keyboard flags: save main, restore alt
646+
kk := &h.coreService.KittyKeyboard
647+
kk.MainFlags = kk.Flags
648+
kk.Flags = kk.AltFlags
649+
kk.MainStack, kk.AltStack = kk.AltStack, kk.MainStack
645650
h.bufferService.Buffers.ActivateAltBuffer(h.eraseAttrData())
646651
h.coreService.IsCursorInitialized = true
647652
h.OnRequestRefreshRowsEmitter.Fire(RowRange{})
@@ -687,12 +692,22 @@ func (h *InputHandler) resetModePrivate(params *Params) bool {
687692
case 1048:
688693
h.RestoreCursor()
689694
case 1049:
695+
// Swap kitty keyboard flags: save alt, restore main
696+
kk := &h.coreService.KittyKeyboard
697+
kk.AltFlags = kk.Flags
698+
kk.Flags = kk.MainFlags
699+
kk.MainStack, kk.AltStack = kk.AltStack, kk.MainStack
690700
h.bufferService.Buffers.ActivateNormalBuffer()
691701
h.RestoreCursor()
692702
h.coreService.IsCursorInitialized = true
693703
h.OnRequestRefreshRowsEmitter.Fire(RowRange{})
694704
h.OnRequestSyncScrollBarEmitter.Fire(struct{}{})
695705
case 47, 1047:
706+
// Swap kitty keyboard flags: save alt, restore main
707+
kk := &h.coreService.KittyKeyboard
708+
kk.AltFlags = kk.Flags
709+
kk.Flags = kk.MainFlags
710+
kk.MainStack, kk.AltStack = kk.AltStack, kk.MainStack
696711
h.bufferService.Buffers.ActivateNormalBuffer()
697712
h.coreService.IsCursorInitialized = true
698713
h.OnRequestRefreshRowsEmitter.Fire(RowRange{})

inputhandler_csi_test.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,4 +799,134 @@ func TestSetResetModeColorSchemeAndWin32(t *testing.T) {
799799
}
800800
}
801801

802+
func TestKittyKeyboardFlagSwapOnAltBuffer(t *testing.T) {
803+
t.Parallel()
804+
805+
type KKState struct {
806+
Flags int
807+
MainFlags int
808+
AltFlags int
809+
}
810+
811+
tests := []struct {
812+
Name string
813+
Setup func(h *InputHandler)
814+
Input string
815+
Expected KKState
816+
}{
817+
{
818+
Name: "DECSET_1049_saves_main_flags_restores_alt",
819+
Setup: func(h *InputHandler) {
820+
h.coreService.KittyKeyboard.Flags = 5
821+
h.coreService.KittyKeyboard.AltFlags = 2
822+
},
823+
Input: "\x1b[?1049h",
824+
Expected: KKState{Flags: 2, MainFlags: 5, AltFlags: 2},
825+
},
826+
{
827+
Name: "DECSET_47_saves_main_flags_restores_alt",
828+
Setup: func(h *InputHandler) {
829+
h.coreService.KittyKeyboard.Flags = 3
830+
h.coreService.KittyKeyboard.AltFlags = 1
831+
},
832+
Input: "\x1b[?47h",
833+
Expected: KKState{Flags: 1, MainFlags: 3, AltFlags: 1},
834+
},
835+
{
836+
Name: "DECSET_1047_saves_main_flags_restores_alt",
837+
Setup: func(h *InputHandler) {
838+
h.coreService.KittyKeyboard.Flags = 7
839+
h.coreService.KittyKeyboard.AltFlags = 0
840+
},
841+
Input: "\x1b[?1047h",
842+
Expected: KKState{Flags: 0, MainFlags: 7, AltFlags: 0},
843+
},
844+
{
845+
Name: "DECRST_1049_saves_alt_flags_restores_main",
846+
Setup: func(h *InputHandler) {
847+
// First switch to alt buffer
848+
h.coreService.KittyKeyboard.Flags = 5
849+
h.ParseString("\x1b[?1049h")
850+
// Now set flags as if app changed them on alt screen
851+
h.coreService.KittyKeyboard.Flags = 9
852+
},
853+
Input: "\x1b[?1049l",
854+
Expected: KKState{Flags: 5, MainFlags: 5, AltFlags: 9},
855+
},
856+
{
857+
Name: "DECRST_47_saves_alt_flags_restores_main",
858+
Setup: func(h *InputHandler) {
859+
h.coreService.KittyKeyboard.Flags = 4
860+
h.ParseString("\x1b[?47h")
861+
h.coreService.KittyKeyboard.Flags = 6
862+
},
863+
Input: "\x1b[?47l",
864+
Expected: KKState{Flags: 4, MainFlags: 4, AltFlags: 6},
865+
},
866+
{
867+
Name: "DECRST_1047_saves_alt_flags_restores_main",
868+
Setup: func(h *InputHandler) {
869+
h.coreService.KittyKeyboard.Flags = 8
870+
h.ParseString("\x1b[?1047h")
871+
h.coreService.KittyKeyboard.Flags = 3
872+
},
873+
Input: "\x1b[?1047l",
874+
Expected: KKState{Flags: 8, MainFlags: 8, AltFlags: 3},
875+
},
876+
{
877+
Name: "round_trip_preserves_flags",
878+
Setup: func(h *InputHandler) {
879+
h.coreService.KittyKeyboard.Flags = 10
880+
h.coreService.KittyKeyboard.AltFlags = 20
881+
},
882+
Input: "\x1b[?1049h\x1b[?1049l",
883+
Expected: KKState{Flags: 10, MainFlags: 10, AltFlags: 20},
884+
},
885+
}
886+
887+
for _, tc := range tests {
888+
t.Run(tc.Name, func(t *testing.T) {
889+
t.Parallel()
890+
h := newTestInputHandler(80, 24)
891+
if tc.Setup != nil {
892+
tc.Setup(h)
893+
}
894+
h.ParseString(tc.Input)
895+
kk := h.coreService.KittyKeyboard
896+
got := KKState{Flags: kk.Flags, MainFlags: kk.MainFlags, AltFlags: kk.AltFlags}
897+
if diff := cmp.Diff(tc.Expected, got); diff != "" {
898+
t.Errorf("mismatch (-want +got):\n%s", diff)
899+
}
900+
})
901+
}
902+
}
802903

904+
func TestKittyKeyboardStackSwapOnAltBuffer(t *testing.T) {
905+
t.Parallel()
906+
h := newTestInputHandler(80, 24)
907+
908+
// Set up main stack
909+
h.coreService.KittyKeyboard.Flags = 1
910+
h.coreService.KittyKeyboard.MainStack = nil
911+
h.coreService.KittyKeyboard.AltStack = []int{10, 20}
912+
913+
// Switch to alt buffer — main stack saved, alt stack restored
914+
h.ParseString("\x1b[?1049h")
915+
kk := h.coreService.KittyKeyboard
916+
if len(kk.AltStack) != 0 {
917+
t.Errorf("expected AltStack to be swapped out (len 0), got len %d", len(kk.AltStack))
918+
}
919+
if len(kk.MainStack) != 2 || kk.MainStack[0] != 10 || kk.MainStack[1] != 20 {
920+
t.Errorf("expected MainStack [10 20], got %v", kk.MainStack)
921+
}
922+
923+
// Switch back to normal buffer — stacks swap back
924+
h.ParseString("\x1b[?1049l")
925+
kk = h.coreService.KittyKeyboard
926+
if len(kk.MainStack) != 0 {
927+
t.Errorf("expected MainStack to be swapped back (len 0), got len %d", len(kk.MainStack))
928+
}
929+
if len(kk.AltStack) != 2 || kk.AltStack[0] != 10 || kk.AltStack[1] != 20 {
930+
t.Errorf("expected AltStack [10 20], got %v", kk.AltStack)
931+
}
932+
}

0 commit comments

Comments
 (0)