/
handler.go
2033 lines (1877 loc) · 55.8 KB
/
handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright 2022 wangqi. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package terminal
import (
"encoding/base64"
"fmt"
"sort"
"strconv"
"strings"
"github.com/ericwq/aprilsh/util"
"github.com/rivo/uniseg"
)
/*
* 64 - VT420 family
* 1 - 132 columns
* 9 - National Replacement Character-sets
* 15 - DEC technical set
* 21 - horizontal scrolling
* 22 - color
*/
const (
DEVICE_ID = "64;1;9;15;21;22c"
)
const (
unused_handlerID = iota
C0_BEL
C0_CR
C0_HT
C0_SI
C0_SO
CSI_CBT
CSI_CHA
CSI_CHT
CSI_CNL
CSI_CPL
CSI_CUB
CSI_CUD
CSI_CUF
CSI_CUP
CSI_CUU
CSI_DCH
CSI_DECIC
CSI_DECDC
CSI_privRM
CSI_DECSCL
CSI_DECSCUSR
CSI_privSM
CSI_DECSTBM
CSI_DECSTR
CSI_ECMA48_SL
CSI_ECMA48_SR
CSI_FocusIn
CSI_FocusOut
CSI_DL
CSI_DSR
CSI_ECH
CSI_ED
CSI_EL
CSI_HPA
CSI_HPR
CSI_ICH
CSI_IL
CSI_priDA
CSI_REP
CSI_RM
CSI_secDA
CSI_SD
CSI_SM
CSI_SU
CSI_SCORC
CSI_SLRM_SCOSC
CSI_SGR
CSI_TBC
CSI_VPA
CSI_VPR
CSI_XTMODKEYS
CSI_XTWINOPS
DCS_DECRQSS
ESC_BI
ESC_DCS
ESC_DECALN
ESC_DECANM
ESC_DECKPAM
ESC_DECKPNM
ESC_DECRC
ESC_DECSC
ESC_DOCS_UTF8
ESC_DOCS_ISO8859_1
ESC_FI
ESC_HTS
ESC_IND
ESC_LS1R
ESC_LS2
ESC_LS2R
ESC_LS3
ESC_LS3R
ESC_NEL
ESC_RI
ESC_RIS
ESC_SS2
ESC_SS3
Graphemes
OSC_4
OSC_52
OSC_0_1_2
OSC_10_11_12_17_19
OSC_112
VT52_EGM
VT52_ID
)
var strHandlerID = [...]string{
"",
"c0_bel",
"c0_cr",
"c0_ht",
"c0_si",
"c0_so",
"csi_cbt",
"csi_cha",
"csi_cht",
"csi_cnl",
"csi_cpl",
"csi_cub",
"csi_cud",
"csi_cuf",
"csi_cup",
"csi_cuu",
"csi_dch",
"csi_decic",
"csi_decdc",
"csi_decrst",
"csi_decscl",
"csi_decscusr",
"csi_decset",
"csi_decstbm",
"csi_decstr",
"csi_ecma48_SL",
"csi_ecma48_SR",
"csi_focus_in",
"csi_focus_out",
"csi_dl",
"csi_dsr",
"csi_ech",
"csi_ed",
"csi_el",
"csi_hpa",
"csi_hpr",
"csi_ich",
"csi_il",
"csi_priDA",
"csi_rep",
"csi_rm",
"csi_secDA",
"csi_sd",
"csi_sm",
"csi_su",
"csi_scorc",
"csi_slrm_scosc",
"csi_sgr",
"csi_tbc",
"csi_vpa",
"csi_vpr",
"csi_xtmodkeys",
"csi_xtwinops",
"dcs_decrqss",
"esc_bi",
"esc_dcs",
"esc_decaln",
"esc_decanm",
"esc_deckpam",
"esc_deckpnm",
"esc_decrc",
"esc_decsc",
"esc_docs_utf_8",
"esc_docs_iso8859_1",
"esc_fi",
"esc_hts",
"esc_ind",
"esc_ls1r",
"esc_ls2",
"esc_ls2r",
"esc_ls3",
"esc_ls3r",
"esc_nel",
"esc_ri",
"esc_ris",
"esc_ss2",
"esc_ss3",
"graphemes",
"osc_4",
"osc_52",
"osc_0_1_2",
"osc_10_11_12_17_19",
"osc_112",
"vt52_egm",
"vt52_id",
}
// Handler is the outcome of parsering input, it can be used to perform control sequence on emulator.
type Handler struct {
// name string // the name of Handler. TODO consider remove this field
id int // handler ID
sequence string // control sequence
ch rune // the last byte
handle func(emu *Emulator) // handle function that will perform control sequnce on emulator
}
func (h *Handler) GetId() int {
return h.id
}
func (h *Handler) GetCh() rune {
return h.ch
}
func restoreSequence(hds []*Handler) string {
var b strings.Builder
for i := range hds {
b.WriteString(hds[i].sequence)
}
return b.String()
}
/*
func (h *Handler) GetSequence() string {
return h.sequence
}
func (h *Handler) Handle(emu *Emulator) {
h.handle(emu)
}
*/
// In the loop, national flag's width got 1+1=2.
/*
func RunesWidth(runes []rune) (width int) {
// return uniseg.StringWidth(string(runes))
// quick pass for iso8859-1
if len(runes) == 1 && runes[0] < 0x00fe {
return 1
}
cond := runewidth.NewCondition()
cond.StrictEmojiNeutral = false
cond.EastAsianWidth = true
// loop for multi rune
width = 0
for i := 0; i < len(runes); i++ {
width += cond.RuneWidth(runes[i])
}
return width
}
*/
// print the graphic char to the emulator
// https://henvic.dev/posts/go-utf8/
// https://pkg.go.dev/golang.org/x/text/encoding/charmap
// https://github.com/rivo/uniseg
func hdl_graphemes(emu *Emulator, chs ...rune) {
w := uniseg.StringWidth(string(chs))
if len(chs) == 1 && emu.charsetState.vtMode {
chs[0] = emu.lookupCharset(chs[0])
}
// fmt.Printf("#hdl_graphemes %q, %U, %t w=%d (%d/%d)\n", chs, chs, emu.lastCol, w, emu.posY, emu.posX)
// the first condition deal with the new graphemes should wrap on next row
// the second condition deal with widh graphemes in special position: posX = nColsEff-1 and width is 2
if (emu.autoWrapMode && emu.lastCol) || (w == 2 && emu.posX == emu.nColsEff-1) {
lastCell := emu.cf.getCellPtr(emu.posY, emu.posX)
if w == 2 && emu.posX == emu.nColsEff-1 {
lastCell.SetEarlyWrap(true) // mark the last chinese cell early wrap case
}
lastCell.wrap = true
hdl_c0_cr(emu)
hdl_c0_lf(emu)
}
// validate lastCol
// fmt.Printf("#hdl_graphemes lastCol at (%d,%d) lastCol=%t\n", emu.posY, emu.posX, emu.lastCol)
// if emu.lastCol && emu.posX != emu.nColsEff-1 {
// emu.lastCol = false
// }
// insert a blank cell for insert mode
if emu.insertMode {
hdl_csi_ich(emu, 1)
}
// print grapheme in current cursor position with default renditions.
c := emu.cf.getCellPtr(emu.posY, emu.posX)
*c = emu.attrs
c.SetContents(chs)
// fmt.Printf("#hdl_graphemes print %q at (%d,%d) %q\n", c, emu.posY, emu.posX, c.renditions.SGR())
/// for double width graphemes
if w == 2 && emu.posX < emu.nColsEff-1 {
// set double width flag
c.SetDoubleWidth(true)
// the cell after double width cell
// set double width continue flag
emu.posX++
emu.cf.getCellPtr(emu.posY, emu.posX).SetDoubleWidthCont(true)
}
// prepare for the next graphemes, move the cursor or set last column flag
// posX is used by current grapheme.
if emu.posX == emu.nColsEff-1 {
emu.lastCol = true
} else {
emu.posX++
}
// fmt.Printf("#hdl_graphemes next at (%d,%d) lastCol=%t\n", emu.posY, emu.posX, emu.lastCol)
}
// func hdl_userbyte(emu *emulator, u UserByte) {
// ret := emu.user.parse(u, emu.framebuffer.DS.ApplicationModeCursorKeys)
// emu.dispatcher.terminalToHost.WriteString(ret)
// }
//
// func hdl_resize(emu *emulator, width, height int) {
// emu.resize(width, height)
// }
// Horizontal Tab (HTS is Ctrl-I).
// move cursor to the next tab position
func hdl_c0_ht(emu *Emulator) {
if emu.posX < emu.nColsEff-1 {
emu.jumpToNextTabStop()
}
}
// Bell (BEL is Ctrl-G).
// ring the bell
func hdl_c0_bel(emu *Emulator) {
emu.ringBell()
}
// Carriage Return (CR is Ctrl-M).
// move cursor to the head of the same row
func hdl_c0_cr(emu *Emulator) {
if emu.originMode == OriginMode_Absolute && emu.posX < emu.hMargin {
emu.posX = 0
} else {
emu.posX = emu.hMargin
}
emu.lastCol = false
}
// Line Feed
// move cursor to the next row, scroll down if necessary.
// if the screen scrolled, erase the line from current position.
func hdl_c0_lf(emu *Emulator) {
if hdl_esc_ind(emu) {
emu.cf.eraseInRow(emu.posY, emu.posX, emu.nColsEff-emu.posX, emu.attrs)
}
}
// SI Switch to Standard Character Set (Ctrl-O is Shift In or LS0).
//
// This invokes the G0 character set (the default) as GL.
// VT200 and up implement LS0.
func hdl_c0_si(emu *Emulator) {
emu.charsetState.gl = 0
}
// SO Switch to Alternate Character Set (Ctrl-N is Shift Out or
//
// LS1). This invokes the G1 character set as GL.
// VT200 and up implement LS1.
func hdl_c0_so(emu *Emulator) {
emu.charsetState.gl = 1
}
// VT52: switch gl to charset DEC special
func hdl_vt52_egm(emu *Emulator) {
resetCharsetState(&emu.charsetState)
emu.charsetState.g[emu.charsetState.gl] = &vt_DEC_Special
}
// VT52: return device id for vt52 emulator
// For a terminal emulating VT52, the identifying sequence should be ESC / Z.
func hdl_vt52_id(emu *Emulator) {
emu.writePty("\x1B/Z")
}
// FF, VT same as LF a.k.a IND Index
// move cursor to the next row, scroll up if necessary.
func hdl_esc_ind(emu *Emulator) (scrolled bool) {
if emu.posY == emu.marginBottom-1 {
// text up, viewpoint down if it reaches the last row in active area
hdl_csi_su(emu, 1)
scrolled = true
} else if emu.posY < emu.nRows-1 {
emu.posY++
emu.lastCol = false
}
return
}
// ESC N
//
// Single Shift Select of G2 Character Set (SS2 is 0x8e), VT220.
// This affects next character only.
func hdl_esc_ss2(emu *Emulator) {
emu.charsetState.ss = 2
}
// ESC O
//
// Single Shift Select of G3 Character Set (SS3 is 0x8f), VT220.
// This affects next character only.
func hdl_esc_ss3(emu *Emulator) {
emu.charsetState.ss = 3
}
// ESC ~ Invoke the G1 Character Set as GR (LS1R), VT100.
func hdl_esc_ls1r(emu *Emulator) {
emu.charsetState.gr = 1
}
// ESC n Invoke the G2 Character Set as GL (LS2).
func hdl_esc_ls2(emu *Emulator) {
emu.charsetState.gl = 2
}
// ESC } Invoke the G2 Character Set as GR (LS2R).
func hdl_esc_ls2r(emu *Emulator) {
emu.charsetState.gr = 2
}
// ESC o Invoke the G3 Character Set as GL (LS3).
func hdl_esc_ls3(emu *Emulator) {
emu.charsetState.gl = 3
}
// ESC | Invoke the G3 Character Set as GR (LS3R).
func hdl_esc_ls3r(emu *Emulator) {
emu.charsetState.gr = 3
}
// ESC % G Select UTF-8 character set, ISO 2022.
// https://en.wikipedia.org/wiki/ISO/IEC_2022#Interaction_with_other_coding_systems
func hdl_esc_docs_utf8(emu *Emulator) {
resetCharsetState(&emu.charsetState)
}
// ESC % @ Select default character set. That is ISO 8859-1 (ISO 2022).
// https://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
func hdl_esc_docs_iso8859_1(emu *Emulator) {
resetCharsetState(&emu.charsetState)
emu.charsetState.g[emu.charsetState.gr] = &vt_ISO_8859_1 // Charset_IsoLatin1
emu.charsetState.vtMode = true
}
// Select G0 ~ G3 character set based on parameter
func hdl_esc_dcs(emu *Emulator, index int, charset *map[byte]rune) {
emu.charsetState.g[index] = charset
if charset != nil {
emu.charsetState.vtMode = true
}
}
// ESC H Tab Set (HTS is 0x88).
// Sets a tab stop in the current column the cursor is in.
func hdl_esc_hts(emu *Emulator) {
emu.tabStops = append(emu.tabStops, emu.posX)
sort.Ints(emu.tabStops)
}
// ESC M Reverse Index (RI is 0x8d).
// reverse index -- like a backwards line feed
func hdl_esc_ri(emu *Emulator) {
if emu.posY == emu.marginTop {
// scroll down 1 row
hdl_csi_sd(emu, 1)
} else if emu.posY > 0 {
emu.posY--
emu.lastCol = false
}
}
// ESC E Next Line (NEL is 0x85).
func hdl_esc_nel(emu *Emulator) {
hdl_esc_ind(emu)
hdl_c0_cr(emu)
}
// ESC c Full Reset (RIS), VT100.
// reset the screen
func hdl_esc_ris(emu *Emulator) {
emu.resetTerminal()
}
// ESC 7 Save Cursor (DECSC), VT100.
func hdl_esc_decsc(emu *Emulator) {
emu.savedCursor_DEC.posX = emu.posX
emu.savedCursor_DEC.posY = emu.posY
emu.savedCursor_DEC.lastCol = emu.lastCol
emu.savedCursor_DEC.attrs = emu.attrs
emu.savedCursor_DEC.originMode = emu.originMode
emu.savedCursor_DEC.charsetState = emu.charsetState
emu.savedCursor_DEC.isSet = true
// fmt.Printf("esc_decsc 501: save DEC: (%d,%d) isSet=%t\n", emu.posY, emu.posX, emu.savedCursor_DEC.isSet)
}
// ESC 8 Restore Cursor (DECRC), VT100.
func hdl_esc_decrc(emu *Emulator) {
if !emu.savedCursor_DEC.isSet {
// emu.logI.Println("Asked to restore cursor (DECRC) but it has not been saved.")
util.Logger.Warn("Asked to restore cursor (DECRC) but it has not been saved")
} else {
emu.posX = emu.savedCursor_DEC.posX
emu.posY = emu.savedCursor_DEC.posY
emu.normalizeCursorPos()
emu.lastCol = emu.savedCursor_DEC.lastCol
emu.attrs = emu.savedCursor_DEC.attrs
emu.originMode = emu.savedCursor_DEC.originMode
emu.charsetState = emu.savedCursor_DEC.charsetState
emu.savedCursor_DEC.isSet = false
// fmt.Printf("esc_decsc 518: restore DEC: (%d,%d) isSet=%t\n", emu.posY, emu.posX, emu.savedCursor_DEC.isSet)
}
}
// ESC # 8 DEC Screen Alignment Test (DECALN), VT100.
// fill the screen with 'E'
func hdl_esc_decaln(emu *Emulator) {
// Save current attrs
origAttrs := emu.attrs
origFg := emu.attrs.renditions.fgColor
origBg := emu.attrs.renditions.bgColor
emu.resetAttrs()
emu.fillScreen('E')
emu.fg = origFg
emu.bg = origBg
emu.attrs = origAttrs
}
// DECFI—Forward Index
// This control function moves the cursor forward one column. If the cursor is
// at the right margin, then all screen data within the margins moves one column
// to the left. The column shifted past the left margin is lost.
func hdl_esc_fi(emu *Emulator) {
arg := 1
if emu.posX < emu.nColsEff-1 {
hdl_csi_cuf(emu, arg)
} else {
hdl_csi_ecma48_SL(emu, arg)
}
}
// DECBI—Back Index
// This control function moves the cursor backward one column. If the cursor is
// at the left margin, then all screen data within the margin moves one column
// to the right. The column that shifted past the right margin is lost.
func hdl_esc_bi(emu *Emulator) {
arg := 1
if emu.posX > emu.hMargin {
hdl_csi_cub(emu, arg)
} else {
hdl_csi_ecma48_SR(emu, arg)
}
}
// DECKPAM—Keypad Application Mode
// DECKPAM enables the numeric keypad to send application sequences to the host.
// DECKPNM enables the numeric keypad to send numeric characters.
// ESC = Send application sequences.
func hdl_esc_deckpam(emu *Emulator) {
emu.keypadMode = KeypadMode_Application
}
// DECKPNM—Keypad Numeric Mode
// DECKPNM enables the keypad to send numeric characters to the host.
// DECKPAM enables the keypad to send application sequences.
// ESC > Send numeric keypad characters.
func hdl_esc_deckpnm(emu *Emulator) {
emu.keypadMode = KeypadMode_Normal
}
// DECANM—ANSI Mode
func hdl_esc_decanm(emu *Emulator, cl CompatibilityLevel) {
emu.setCompatLevel(cl)
}
// CSI Ps g Tab Clear (TBC).
//
// Ps = 0 ⇒ Clear Current Column (default).
// Ps = 3 ⇒ Clear All.
func hdl_csi_tbc(emu *Emulator, cmd int) {
switch cmd {
case 0: // clear this tab stop
idx := sort.SearchInts(emu.tabStops, emu.posX)
if idx < len(emu.tabStops) && emu.tabStops[idx] == emu.posX {
// posX is present at tabStops[idx]
emu.tabStops = RemoveIndex(emu.tabStops, idx)
}
case 3: // clear all tab stops
emu.tabStops = make([]int, 0)
default:
}
}
// CSI Ps I Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
// Advance the cursor to the next column (in the same row) with a tab stop.
// If there are no more tab stops, move to the last column in the row.
func hdl_csi_cht(emu *Emulator, count int) {
if count == 1 {
hdl_c0_ht(emu)
} else {
for k := 0; k < count; k++ {
emu.jumpToNextTabStop()
}
}
}
// CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
// Move the cursor to the previous column (in the same row) with a tab stop.
// If there are no more tab stops, moves the cursor to the first column.
// If the cursor is in the first column, doesn’t move the cursor.
func hdl_csi_cbt(emu *Emulator, count int) {
for k := 0; k < count; k++ {
if len(emu.tabStops) == 0 {
if emu.posX > 0 && emu.posX%8 == 0 {
emu.posX -= 8
} else {
emu.posX = (emu.posX / 8) * 8
}
} else {
// Set posX to previous tab stop
nextTabIdx := LowerBound(emu.tabStops, emu.posX)
if nextTabIdx-1 >= 0 {
emu.posX = emu.tabStops[nextTabIdx-1]
} else {
emu.posX = 0
}
emu.lastCol = false
}
}
}
// CSI Ps @ Insert Ps (Blank) Character(s) (default = 1) (ICH).
func hdl_csi_ich(emu *Emulator, arg int) {
if emu.isCursorInsideMargins() {
length := emu.nColsEff - emu.posX
arg = min(arg, length)
length -= arg
if emu.cf.getCell(emu.posY, emu.posX+arg+length-1).wrap {
// maintain wrap bit invariance at EOL
emu.cf.getCellPtr(emu.posY, emu.posX+arg+length-1).wrap = false
if length != 0 {
emu.cf.getCellPtr(emu.posY, emu.posX+length-1).wrap = true
} // TODO add logic for length ==0
}
emu.cf.moveInRow(emu.posY, emu.posX+arg, emu.posX, length)
emu.cf.eraseInRow(emu.posY, emu.posX, arg, emu.attrs)
}
emu.lastCol = false
}
// CSI Ps J Erase in Display (ED), VT100.
// * Ps = 0 ⇒ Erase Below (default).
// * Ps = 1 ⇒ Erase Above.
// * Ps = 2 ⇒ Erase All.
// * Ps = 3 ⇒ Erase Saved Lines, xterm.
func hdl_csi_ed(emu *Emulator, cmd int) {
emu.normalizeCursorPos()
switch cmd {
case 0: // clear from cursor to end of screen
emu.cf.eraseInRow(emu.posY, emu.posX, emu.nCols-emu.posX, emu.attrs)
for pY := emu.posY + 1; pY < emu.nRows; pY++ {
emu.eraseRow(pY)
}
case 1: // clear from beginning of screen to cursor
for pY := 0; pY < emu.posY; pY++ {
emu.eraseRow(pY)
}
emu.cf.eraseInRow(emu.posY, 0, emu.posX+1, emu.attrs)
case 3: // clear entire screen including scrollback buffer (xterm)
emu.cf.dropScrollbackHistory()
fallthrough
case 2: // clear entire screen
for pY := 0; pY < emu.nRows; pY++ {
emu.eraseRow(pY)
}
default:
// emu.logI.Printf("Erase in Display with illegal param: %d\n", cmd)
util.Logger.Info("Erase in Display with illegal param", "cmd", cmd)
}
}
// CSI Ps K Erase in Line (EL), VT100.
// * Ps = 0 ⇒ Erase to Right (default).
// * Ps = 1 ⇒ Erase to Left.
// * Ps = 2 ⇒ Erase All.
func hdl_csi_el(emu *Emulator, cmd int) {
emu.normalizeCursorPos()
switch cmd {
case 0: // clear from cursor to end of line
emu.cf.eraseInRow(emu.posY, emu.posX, emu.nCols-emu.posX, emu.attrs)
case 1: // clear from cursor to beginning of line
emu.cf.eraseInRow(emu.posY, 0, emu.posX+1, emu.attrs)
case 2: // clear entire line
emu.cf.eraseInRow(emu.posY, 0, emu.nCols, emu.attrs)
default:
// emu.logI.Printf("Erase in Line with illegal param: %d\n", cmd)
util.Logger.Info("Erase in Line with illegal param", "cmd", cmd)
}
}
// CSI Ps L Insert Ps Line(s) (default = 1) (IL).
// insert N lines in cursor position
func hdl_csi_il(emu *Emulator, lines int) {
if emu.isCursorInsideMargins() {
lines = min(lines, emu.marginBottom-emu.posY)
emu.insertRows(emu.posY, lines)
hdl_c0_cr(emu)
}
}
// CSI Ps M Delete Ps Line(s) (default = 1) (DL).
// delete N lines in cursor position
func hdl_csi_dl(emu *Emulator, lines int) {
if emu.isCursorInsideMargins() {
lines = min(lines, emu.marginBottom-emu.posY)
emu.deleteRows(emu.posY, lines)
hdl_c0_cr(emu)
}
}
// CSI I FocusIn
// CSI O FocusOut
func hdl_csi_focus(emu *Emulator, hasFocus bool) {
if emu.mouseTrk.focusEventMode {
// if hasFocus {
// emu.writePty("\x1B[I")
// } else {
// emu.writePty("\x1B[O")
// }
emu.setHasFocus(hasFocus)
}
}
// CSI Ps P Delete Ps Character(s) (default = 1) (DCH).
func hdl_csi_dch(emu *Emulator, arg int) {
if emu.isCursorInsideMargins() {
length := emu.nColsEff - emu.posX
arg = min(arg, length)
// arg = calculateCellNum(emu, arg)
length -= arg
// fmt.Printf("#hdl_csi_dch posX=%d, arg=%d\n", emu.posX, arg)
emu.cf.moveInRow(emu.posY, emu.posX, emu.posX+arg, length)
emu.cf.eraseInRow(emu.posY, emu.posX+length, arg, emu.attrs)
}
emu.lastCol = false
}
// CSI Ps S Scroll up Ps lines (default = 1) (SU), VT420, ECMA-48.
func hdl_csi_su(emu *Emulator, arg int) {
if emu.horizMarginMode {
arg = min(arg, emu.marginBottom-emu.marginTop)
emu.deleteRows(emu.marginTop, arg)
} else {
emu.cf.scrollUp(arg)
emu.eraseRows(emu.marginBottom-arg, arg)
emu.lastCol = false
}
}
// CSI Ps T Scroll down Ps lines (default = 1) (SD), VT420.
func hdl_csi_sd(emu *Emulator, arg int) {
if emu.horizMarginMode {
arg = min(arg, emu.marginBottom-emu.marginTop)
emu.insertRows(emu.marginTop, arg)
} else {
emu.cf.scrollDown(arg)
emu.eraseRows(emu.marginTop, arg)
emu.lastCol = false
}
}
// CSI Ps X Erase Ps Character(s) (default = 1) (ECH).
func hdl_csi_ech(emu *Emulator, arg int) {
length := emu.nColsEff - emu.posX
arg = min(arg, length)
emu.cf.eraseInRow(emu.posY, emu.posX, arg, emu.attrs)
emu.lastCol = false
}
// CSI Ps c Send Device Attributes (Primary DA).
// CSI ? 6 2 ; Ps c ("VT220")
// DA response
func hdl_csi_priDA(emu *Emulator) {
// mosh only reply "\x1B[?62c" plain vt220
resp := fmt.Sprintf("\x1B[?%s", DEVICE_ID)
emu.writePty(resp)
}
// CSI > Ps c Send Device Attributes (Secondary DA).
// Ps = 0 or omitted ⇒ request the terminal's identification code.
// CSI > Pp ; Pv ; Pc c
// Pp = 1 ⇒ "VT220".
// Pv is the firmware version.
// Pc indicates the ROM cartridge registration number and is always zero.
func hdl_csi_secDA(emu *Emulator) {
// mosh only reply "\033[>1;10;0c" plain vt220
resp := "\x1B[>64;0;0c" // VT520
emu.writePty(resp)
}
// CSI Ps d Line Position Absolute [row] (default = [1,column]) (VPA).
// Move cursor to line Pn.
func hdl_csi_vpa(emu *Emulator, row int) {
row = max(1, min(row, emu.nRows))
emu.posY = row - 1
emu.lastCol = false
}
// CSI Ps e Line Position Relative [rows] (default = [row+1,column]) (VPR).
// Move cursor to the n-th line relative to active row
func hdl_csi_vpr(emu *Emulator, row int) {
row += emu.posY + 1
row = max(1, min(row, emu.nRows))
emu.posY = row - 1
emu.lastCol = false
}
// Move the active position to the n-th character of the active line.
// CSI Ps G Cursor Character Absolute [column] (default = [row,1]) (CHA).
func hdl_csi_cha(emu *Emulator, count int) {
count = max(1, min(count, emu.nCols))
emu.posX = count - 1
emu.lastCol = false
}
// Move the active position to the n-th character of the active line.
// CSI Ps ` Character Position Absolute [column] (default = [row,1]) (HPA).
// same as CHA
func hdl_csi_hpa(emu *Emulator, count int) {
hdl_csi_cha(emu, count)
}
// CSI Ps a Character Position Relative [columns] (default = [row,col+1]) (HPR).
// move to the n-th character relative to the active position
func hdl_csi_hpr(emu *Emulator, arg int) {
hdl_csi_cha(emu, emu.posX+arg+1)
}
// CSI Ps A Cursor Up Ps Times (default = 1) (CUU).
func hdl_csi_cuu(emu *Emulator, num int) {
if emu.posY >= emu.marginTop {
num = min(num, emu.posY-emu.marginTop)
} else {
num = min(num, emu.posY)
}
emu.posY -= num
emu.lastCol = false
}
// CSI Ps B Cursor Down Ps Times (default = 1) (CUD).
func hdl_csi_cud(emu *Emulator, num int) {
if emu.posY < emu.marginBottom {
num = min(num, emu.marginBottom-emu.posY-1)
} else {
num = min(num, emu.nRows-emu.posY-1)
}
emu.posY += num
emu.lastCol = false
}
// CSI Ps C Cursor Forward Ps Times (default = 1) (CUF).
func hdl_csi_cuf(emu *Emulator, num int) {
num = min(num, emu.nColsEff-emu.posX-1)
emu.posX += num
// emu.posX += calculateCellNum(emu, num)
emu.lastCol = false
}
// CSI Ps D Cursor Backward Ps Times (default = 1) (CUB).
func hdl_csi_cub(emu *Emulator, num int) {
// fmt.Printf("hdl_csi_cub num=%d, posX=%d, hMargin=%d\n", num, emu.posX, emu.hMargin)
if emu.posX >= emu.hMargin {
num = min(num, emu.posX-emu.hMargin)
} else {
num = min(num, emu.posX)
}
if emu.posX == emu.nColsEff {
num = min(num+1, emu.posX)
}
emu.posX -= num
// fmt.Printf("hdl_csi_cub -num=%d\n", -num)
// emu.posX += calculateCellNum(emu, -num)
emu.lastCol = false
}
/*
// calculate raw cell number with the consideration of wide grapheme and
// regular grapheme. one wide grapheme takes two raw cells. one regular
// grapheme takes one cell, count is the number of graphemes.
//
// count >0 , count raw cell to right.
// count <0 , count raw cell to left.
func calculateCellNum(emu *Emulator, count int) int {
oldX := emu.posX
currentX := emu.posX // the start position
// var cell Cell
for i := 0; i < Abs(count); i++ {
// fmt.Printf("#calculateCellNum currentX=%d\n", currentX)
if count > 0 { // calculate to the right
if currentX >= emu.nColsEff-1 {
currentX = emu.nColsEff
break
}
// emu.GetCell(emu.posY, currentX+1)
// cell = emu.GetCell(emu.posY, currentX+1)
// if cell.dwidth || cell.dwidthCont {
// currentX += 2
// } else {
// currentX++
// }
currentX++
} else { // calculate to the left
// fmt.Printf("#calculateCellNum currentX=%d, count=%d, emu.hMargin=%d\n", currentX, count, emu.hMargin)
// if currentX <= emu.hMargin {
// currentX = emu.hMargin
// break
// }
// emu.GetCell(emu.posY, currentX-1)
// cell = emu.GetCell(emu.posY, currentX-1)
// if cell.dwidthCont || cell.dwidth {
// currentX -= 2
// } else {
// currentX--
// }
currentX--
}
}
return currentX - oldX
}
*/
// CSI Ps ; Ps H Cursor Position [row;column] (default = [1,1]) (CUP).
func hdl_csi_cup(emu *Emulator, row int, col int) {
switch emu.originMode {
case OriginMode_Absolute:
row = max(1, min(row, emu.nRows)) - 1
case OriginMode_ScrollingRegion:
row = max(1, min(row, emu.marginBottom)) - 1
row += emu.marginTop
}
col = max(1, min(col, emu.nCols)) - 1
emu.posX = col
emu.posY = row
emu.lastCol = false
// emu.logT.Printf("Cursor positioned to (%d,%d)\n", emu.posY, emu.posX)
}
// CSI Ps n Device Status Report (DSR).
//
// Ps = 5 ⇒ Status Report. Result ("OK") is CSI 0 n
// Ps = 6 ⇒ Report Cursor Position (CPR) [row;column]. Result is CSI r ; c R
func hdl_csi_dsr(emu *Emulator, cmd int) {
switch cmd {
case 5: // device status report requested
emu.writePty("\x1B[0n") // device OK
case 6: // report of active position requested
resp := ""
if emu.originMode == OriginMode_Absolute {
resp = fmt.Sprintf("\x1B[%d;%dR", emu.posY+1, emu.posX+1)
} else {
// scrolling region mode
resp = fmt.Sprintf("\x1B[%d;%dR", emu.posY-emu.marginTop+1, emu.posX+1)
}
emu.writePty(resp)
default:
}