Tizen_4.0 base
[platform/upstream/docker-engine.git] / vendor / github.com / Azure / go-ansiterm / winterm / win_event_handler.go
1 // +build windows
2
3 package winterm
4
5 import (
6         "bytes"
7         "io/ioutil"
8         "os"
9         "strconv"
10
11         "github.com/Azure/go-ansiterm"
12         "github.com/Sirupsen/logrus"
13 )
14
15 var logger *logrus.Logger
16
17 type windowsAnsiEventHandler struct {
18         fd             uintptr
19         file           *os.File
20         infoReset      *CONSOLE_SCREEN_BUFFER_INFO
21         sr             scrollRegion
22         buffer         bytes.Buffer
23         attributes     uint16
24         inverted       bool
25         wrapNext       bool
26         drewMarginByte bool
27         originMode     bool
28         marginByte     byte
29         curInfo        *CONSOLE_SCREEN_BUFFER_INFO
30         curPos         COORD
31 }
32
33 func CreateWinEventHandler(fd uintptr, file *os.File) ansiterm.AnsiEventHandler {
34         logFile := ioutil.Discard
35
36         if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
37                 logFile, _ = os.Create("winEventHandler.log")
38         }
39
40         logger = &logrus.Logger{
41                 Out:       logFile,
42                 Formatter: new(logrus.TextFormatter),
43                 Level:     logrus.DebugLevel,
44         }
45
46         infoReset, err := GetConsoleScreenBufferInfo(fd)
47         if err != nil {
48                 return nil
49         }
50
51         return &windowsAnsiEventHandler{
52                 fd:         fd,
53                 file:       file,
54                 infoReset:  infoReset,
55                 attributes: infoReset.Attributes,
56         }
57 }
58
59 type scrollRegion struct {
60         top    int16
61         bottom int16
62 }
63
64 // simulateLF simulates a LF or CR+LF by scrolling if necessary to handle the
65 // current cursor position and scroll region settings, in which case it returns
66 // true. If no special handling is necessary, then it does nothing and returns
67 // false.
68 //
69 // In the false case, the caller should ensure that a carriage return
70 // and line feed are inserted or that the text is otherwise wrapped.
71 func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
72         if h.wrapNext {
73                 if err := h.Flush(); err != nil {
74                         return false, err
75                 }
76                 h.clearWrap()
77         }
78         pos, info, err := h.getCurrentInfo()
79         if err != nil {
80                 return false, err
81         }
82         sr := h.effectiveSr(info.Window)
83         if pos.Y == sr.bottom {
84                 // Scrolling is necessary. Let Windows automatically scroll if the scrolling region
85                 // is the full window.
86                 if sr.top == info.Window.Top && sr.bottom == info.Window.Bottom {
87                         if includeCR {
88                                 pos.X = 0
89                                 h.updatePos(pos)
90                         }
91                         return false, nil
92                 }
93
94                 // A custom scroll region is active. Scroll the window manually to simulate
95                 // the LF.
96                 if err := h.Flush(); err != nil {
97                         return false, err
98                 }
99                 logger.Info("Simulating LF inside scroll region")
100                 if err := h.scrollUp(1); err != nil {
101                         return false, err
102                 }
103                 if includeCR {
104                         pos.X = 0
105                         if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
106                                 return false, err
107                         }
108                 }
109                 return true, nil
110
111         } else if pos.Y < info.Window.Bottom {
112                 // Let Windows handle the LF.
113                 pos.Y++
114                 if includeCR {
115                         pos.X = 0
116                 }
117                 h.updatePos(pos)
118                 return false, nil
119         } else {
120                 // The cursor is at the bottom of the screen but outside the scroll
121                 // region. Skip the LF.
122                 logger.Info("Simulating LF outside scroll region")
123                 if includeCR {
124                         if err := h.Flush(); err != nil {
125                                 return false, err
126                         }
127                         pos.X = 0
128                         if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
129                                 return false, err
130                         }
131                 }
132                 return true, nil
133         }
134 }
135
136 // executeLF executes a LF without a CR.
137 func (h *windowsAnsiEventHandler) executeLF() error {
138         handled, err := h.simulateLF(false)
139         if err != nil {
140                 return err
141         }
142         if !handled {
143                 // Windows LF will reset the cursor column position. Write the LF
144                 // and restore the cursor position.
145                 pos, _, err := h.getCurrentInfo()
146                 if err != nil {
147                         return err
148                 }
149                 h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED)
150                 if pos.X != 0 {
151                         if err := h.Flush(); err != nil {
152                                 return err
153                         }
154                         logger.Info("Resetting cursor position for LF without CR")
155                         if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
156                                 return err
157                         }
158                 }
159         }
160         return nil
161 }
162
163 func (h *windowsAnsiEventHandler) Print(b byte) error {
164         if h.wrapNext {
165                 h.buffer.WriteByte(h.marginByte)
166                 h.clearWrap()
167                 if _, err := h.simulateLF(true); err != nil {
168                         return err
169                 }
170         }
171         pos, info, err := h.getCurrentInfo()
172         if err != nil {
173                 return err
174         }
175         if pos.X == info.Size.X-1 {
176                 h.wrapNext = true
177                 h.marginByte = b
178         } else {
179                 pos.X++
180                 h.updatePos(pos)
181                 h.buffer.WriteByte(b)
182         }
183         return nil
184 }
185
186 func (h *windowsAnsiEventHandler) Execute(b byte) error {
187         switch b {
188         case ansiterm.ANSI_TAB:
189                 logger.Info("Execute(TAB)")
190                 // Move to the next tab stop, but preserve auto-wrap if already set.
191                 if !h.wrapNext {
192                         pos, info, err := h.getCurrentInfo()
193                         if err != nil {
194                                 return err
195                         }
196                         pos.X = (pos.X + 8) - pos.X%8
197                         if pos.X >= info.Size.X {
198                                 pos.X = info.Size.X - 1
199                         }
200                         if err := h.Flush(); err != nil {
201                                 return err
202                         }
203                         if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
204                                 return err
205                         }
206                 }
207                 return nil
208
209         case ansiterm.ANSI_BEL:
210                 h.buffer.WriteByte(ansiterm.ANSI_BEL)
211                 return nil
212
213         case ansiterm.ANSI_BACKSPACE:
214                 if h.wrapNext {
215                         if err := h.Flush(); err != nil {
216                                 return err
217                         }
218                         h.clearWrap()
219                 }
220                 pos, _, err := h.getCurrentInfo()
221                 if err != nil {
222                         return err
223                 }
224                 if pos.X > 0 {
225                         pos.X--
226                         h.updatePos(pos)
227                         h.buffer.WriteByte(ansiterm.ANSI_BACKSPACE)
228                 }
229                 return nil
230
231         case ansiterm.ANSI_VERTICAL_TAB, ansiterm.ANSI_FORM_FEED:
232                 // Treat as true LF.
233                 return h.executeLF()
234
235         case ansiterm.ANSI_LINE_FEED:
236                 // Simulate a CR and LF for now since there is no way in go-ansiterm
237                 // to tell if the LF should include CR (and more things break when it's
238                 // missing than when it's incorrectly added).
239                 handled, err := h.simulateLF(true)
240                 if handled || err != nil {
241                         return err
242                 }
243                 return h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED)
244
245         case ansiterm.ANSI_CARRIAGE_RETURN:
246                 if h.wrapNext {
247                         if err := h.Flush(); err != nil {
248                                 return err
249                         }
250                         h.clearWrap()
251                 }
252                 pos, _, err := h.getCurrentInfo()
253                 if err != nil {
254                         return err
255                 }
256                 if pos.X != 0 {
257                         pos.X = 0
258                         h.updatePos(pos)
259                         h.buffer.WriteByte(ansiterm.ANSI_CARRIAGE_RETURN)
260                 }
261                 return nil
262
263         default:
264                 return nil
265         }
266 }
267
268 func (h *windowsAnsiEventHandler) CUU(param int) error {
269         if err := h.Flush(); err != nil {
270                 return err
271         }
272         logger.Infof("CUU: [%v]", []string{strconv.Itoa(param)})
273         h.clearWrap()
274         return h.moveCursorVertical(-param)
275 }
276
277 func (h *windowsAnsiEventHandler) CUD(param int) error {
278         if err := h.Flush(); err != nil {
279                 return err
280         }
281         logger.Infof("CUD: [%v]", []string{strconv.Itoa(param)})
282         h.clearWrap()
283         return h.moveCursorVertical(param)
284 }
285
286 func (h *windowsAnsiEventHandler) CUF(param int) error {
287         if err := h.Flush(); err != nil {
288                 return err
289         }
290         logger.Infof("CUF: [%v]", []string{strconv.Itoa(param)})
291         h.clearWrap()
292         return h.moveCursorHorizontal(param)
293 }
294
295 func (h *windowsAnsiEventHandler) CUB(param int) error {
296         if err := h.Flush(); err != nil {
297                 return err
298         }
299         logger.Infof("CUB: [%v]", []string{strconv.Itoa(param)})
300         h.clearWrap()
301         return h.moveCursorHorizontal(-param)
302 }
303
304 func (h *windowsAnsiEventHandler) CNL(param int) error {
305         if err := h.Flush(); err != nil {
306                 return err
307         }
308         logger.Infof("CNL: [%v]", []string{strconv.Itoa(param)})
309         h.clearWrap()
310         return h.moveCursorLine(param)
311 }
312
313 func (h *windowsAnsiEventHandler) CPL(param int) error {
314         if err := h.Flush(); err != nil {
315                 return err
316         }
317         logger.Infof("CPL: [%v]", []string{strconv.Itoa(param)})
318         h.clearWrap()
319         return h.moveCursorLine(-param)
320 }
321
322 func (h *windowsAnsiEventHandler) CHA(param int) error {
323         if err := h.Flush(); err != nil {
324                 return err
325         }
326         logger.Infof("CHA: [%v]", []string{strconv.Itoa(param)})
327         h.clearWrap()
328         return h.moveCursorColumn(param)
329 }
330
331 func (h *windowsAnsiEventHandler) VPA(param int) error {
332         if err := h.Flush(); err != nil {
333                 return err
334         }
335         logger.Infof("VPA: [[%d]]", param)
336         h.clearWrap()
337         info, err := GetConsoleScreenBufferInfo(h.fd)
338         if err != nil {
339                 return err
340         }
341         window := h.getCursorWindow(info)
342         position := info.CursorPosition
343         position.Y = window.Top + int16(param) - 1
344         return h.setCursorPosition(position, window)
345 }
346
347 func (h *windowsAnsiEventHandler) CUP(row int, col int) error {
348         if err := h.Flush(); err != nil {
349                 return err
350         }
351         logger.Infof("CUP: [[%d %d]]", row, col)
352         h.clearWrap()
353         info, err := GetConsoleScreenBufferInfo(h.fd)
354         if err != nil {
355                 return err
356         }
357
358         window := h.getCursorWindow(info)
359         position := COORD{window.Left + int16(col) - 1, window.Top + int16(row) - 1}
360         return h.setCursorPosition(position, window)
361 }
362
363 func (h *windowsAnsiEventHandler) HVP(row int, col int) error {
364         if err := h.Flush(); err != nil {
365                 return err
366         }
367         logger.Infof("HVP: [[%d %d]]", row, col)
368         h.clearWrap()
369         return h.CUP(row, col)
370 }
371
372 func (h *windowsAnsiEventHandler) DECTCEM(visible bool) error {
373         if err := h.Flush(); err != nil {
374                 return err
375         }
376         logger.Infof("DECTCEM: [%v]", []string{strconv.FormatBool(visible)})
377         h.clearWrap()
378         return nil
379 }
380
381 func (h *windowsAnsiEventHandler) DECOM(enable bool) error {
382         if err := h.Flush(); err != nil {
383                 return err
384         }
385         logger.Infof("DECOM: [%v]", []string{strconv.FormatBool(enable)})
386         h.clearWrap()
387         h.originMode = enable
388         return h.CUP(1, 1)
389 }
390
391 func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
392         if err := h.Flush(); err != nil {
393                 return err
394         }
395         logger.Infof("DECCOLM: [%v]", []string{strconv.FormatBool(use132)})
396         h.clearWrap()
397         if err := h.ED(2); err != nil {
398                 return err
399         }
400         info, err := GetConsoleScreenBufferInfo(h.fd)
401         if err != nil {
402                 return err
403         }
404         targetWidth := int16(80)
405         if use132 {
406                 targetWidth = 132
407         }
408         if info.Size.X < targetWidth {
409                 if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
410                         logger.Info("set buffer failed:", err)
411                         return err
412                 }
413         }
414         window := info.Window
415         window.Left = 0
416         window.Right = targetWidth - 1
417         if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
418                 logger.Info("set window failed:", err)
419                 return err
420         }
421         if info.Size.X > targetWidth {
422                 if err := SetConsoleScreenBufferSize(h.fd, COORD{targetWidth, info.Size.Y}); err != nil {
423                         logger.Info("set buffer failed:", err)
424                         return err
425                 }
426         }
427         return SetConsoleCursorPosition(h.fd, COORD{0, 0})
428 }
429
430 func (h *windowsAnsiEventHandler) ED(param int) error {
431         if err := h.Flush(); err != nil {
432                 return err
433         }
434         logger.Infof("ED: [%v]", []string{strconv.Itoa(param)})
435         h.clearWrap()
436
437         // [J  -- Erases from the cursor to the end of the screen, including the cursor position.
438         // [1J -- Erases from the beginning of the screen to the cursor, including the cursor position.
439         // [2J -- Erases the complete display. The cursor does not move.
440         // Notes:
441         // -- Clearing the entire buffer, versus just the Window, works best for Windows Consoles
442
443         info, err := GetConsoleScreenBufferInfo(h.fd)
444         if err != nil {
445                 return err
446         }
447
448         var start COORD
449         var end COORD
450
451         switch param {
452         case 0:
453                 start = info.CursorPosition
454                 end = COORD{info.Size.X - 1, info.Size.Y - 1}
455
456         case 1:
457                 start = COORD{0, 0}
458                 end = info.CursorPosition
459
460         case 2:
461                 start = COORD{0, 0}
462                 end = COORD{info.Size.X - 1, info.Size.Y - 1}
463         }
464
465         err = h.clearRange(h.attributes, start, end)
466         if err != nil {
467                 return err
468         }
469
470         // If the whole buffer was cleared, move the window to the top while preserving
471         // the window-relative cursor position.
472         if param == 2 {
473                 pos := info.CursorPosition
474                 window := info.Window
475                 pos.Y -= window.Top
476                 window.Bottom -= window.Top
477                 window.Top = 0
478                 if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
479                         return err
480                 }
481                 if err := SetConsoleWindowInfo(h.fd, true, window); err != nil {
482                         return err
483                 }
484         }
485
486         return nil
487 }
488
489 func (h *windowsAnsiEventHandler) EL(param int) error {
490         if err := h.Flush(); err != nil {
491                 return err
492         }
493         logger.Infof("EL: [%v]", strconv.Itoa(param))
494         h.clearWrap()
495
496         // [K  -- Erases from the cursor to the end of the line, including the cursor position.
497         // [1K -- Erases from the beginning of the line to the cursor, including the cursor position.
498         // [2K -- Erases the complete line.
499
500         info, err := GetConsoleScreenBufferInfo(h.fd)
501         if err != nil {
502                 return err
503         }
504
505         var start COORD
506         var end COORD
507
508         switch param {
509         case 0:
510                 start = info.CursorPosition
511                 end = COORD{info.Size.X, info.CursorPosition.Y}
512
513         case 1:
514                 start = COORD{0, info.CursorPosition.Y}
515                 end = info.CursorPosition
516
517         case 2:
518                 start = COORD{0, info.CursorPosition.Y}
519                 end = COORD{info.Size.X, info.CursorPosition.Y}
520         }
521
522         err = h.clearRange(h.attributes, start, end)
523         if err != nil {
524                 return err
525         }
526
527         return nil
528 }
529
530 func (h *windowsAnsiEventHandler) IL(param int) error {
531         if err := h.Flush(); err != nil {
532                 return err
533         }
534         logger.Infof("IL: [%v]", strconv.Itoa(param))
535         h.clearWrap()
536         return h.insertLines(param)
537 }
538
539 func (h *windowsAnsiEventHandler) DL(param int) error {
540         if err := h.Flush(); err != nil {
541                 return err
542         }
543         logger.Infof("DL: [%v]", strconv.Itoa(param))
544         h.clearWrap()
545         return h.deleteLines(param)
546 }
547
548 func (h *windowsAnsiEventHandler) ICH(param int) error {
549         if err := h.Flush(); err != nil {
550                 return err
551         }
552         logger.Infof("ICH: [%v]", strconv.Itoa(param))
553         h.clearWrap()
554         return h.insertCharacters(param)
555 }
556
557 func (h *windowsAnsiEventHandler) DCH(param int) error {
558         if err := h.Flush(); err != nil {
559                 return err
560         }
561         logger.Infof("DCH: [%v]", strconv.Itoa(param))
562         h.clearWrap()
563         return h.deleteCharacters(param)
564 }
565
566 func (h *windowsAnsiEventHandler) SGR(params []int) error {
567         if err := h.Flush(); err != nil {
568                 return err
569         }
570         strings := []string{}
571         for _, v := range params {
572                 strings = append(strings, strconv.Itoa(v))
573         }
574
575         logger.Infof("SGR: [%v]", strings)
576
577         if len(params) <= 0 {
578                 h.attributes = h.infoReset.Attributes
579                 h.inverted = false
580         } else {
581                 for _, attr := range params {
582
583                         if attr == ansiterm.ANSI_SGR_RESET {
584                                 h.attributes = h.infoReset.Attributes
585                                 h.inverted = false
586                                 continue
587                         }
588
589                         h.attributes, h.inverted = collectAnsiIntoWindowsAttributes(h.attributes, h.inverted, h.infoReset.Attributes, int16(attr))
590                 }
591         }
592
593         attributes := h.attributes
594         if h.inverted {
595                 attributes = invertAttributes(attributes)
596         }
597         err := SetConsoleTextAttribute(h.fd, attributes)
598         if err != nil {
599                 return err
600         }
601
602         return nil
603 }
604
605 func (h *windowsAnsiEventHandler) SU(param int) error {
606         if err := h.Flush(); err != nil {
607                 return err
608         }
609         logger.Infof("SU: [%v]", []string{strconv.Itoa(param)})
610         h.clearWrap()
611         return h.scrollUp(param)
612 }
613
614 func (h *windowsAnsiEventHandler) SD(param int) error {
615         if err := h.Flush(); err != nil {
616                 return err
617         }
618         logger.Infof("SD: [%v]", []string{strconv.Itoa(param)})
619         h.clearWrap()
620         return h.scrollDown(param)
621 }
622
623 func (h *windowsAnsiEventHandler) DA(params []string) error {
624         logger.Infof("DA: [%v]", params)
625         // DA cannot be implemented because it must send data on the VT100 input stream,
626         // which is not available to go-ansiterm.
627         return nil
628 }
629
630 func (h *windowsAnsiEventHandler) DECSTBM(top int, bottom int) error {
631         if err := h.Flush(); err != nil {
632                 return err
633         }
634         logger.Infof("DECSTBM: [%d, %d]", top, bottom)
635
636         // Windows is 0 indexed, Linux is 1 indexed
637         h.sr.top = int16(top - 1)
638         h.sr.bottom = int16(bottom - 1)
639
640         // This command also moves the cursor to the origin.
641         h.clearWrap()
642         return h.CUP(1, 1)
643 }
644
645 func (h *windowsAnsiEventHandler) RI() error {
646         if err := h.Flush(); err != nil {
647                 return err
648         }
649         logger.Info("RI: []")
650         h.clearWrap()
651
652         info, err := GetConsoleScreenBufferInfo(h.fd)
653         if err != nil {
654                 return err
655         }
656
657         sr := h.effectiveSr(info.Window)
658         if info.CursorPosition.Y == sr.top {
659                 return h.scrollDown(1)
660         }
661
662         return h.moveCursorVertical(-1)
663 }
664
665 func (h *windowsAnsiEventHandler) IND() error {
666         logger.Info("IND: []")
667         return h.executeLF()
668 }
669
670 func (h *windowsAnsiEventHandler) Flush() error {
671         h.curInfo = nil
672         if h.buffer.Len() > 0 {
673                 logger.Infof("Flush: [%s]", h.buffer.Bytes())
674                 if _, err := h.buffer.WriteTo(h.file); err != nil {
675                         return err
676                 }
677         }
678
679         if h.wrapNext && !h.drewMarginByte {
680                 logger.Infof("Flush: drawing margin byte '%c'", h.marginByte)
681
682                 info, err := GetConsoleScreenBufferInfo(h.fd)
683                 if err != nil {
684                         return err
685                 }
686
687                 charInfo := []CHAR_INFO{{UnicodeChar: uint16(h.marginByte), Attributes: info.Attributes}}
688                 size := COORD{1, 1}
689                 position := COORD{0, 0}
690                 region := SMALL_RECT{Left: info.CursorPosition.X, Top: info.CursorPosition.Y, Right: info.CursorPosition.X, Bottom: info.CursorPosition.Y}
691                 if err := WriteConsoleOutput(h.fd, charInfo, size, position, &region); err != nil {
692                         return err
693                 }
694                 h.drewMarginByte = true
695         }
696         return nil
697 }
698
699 // cacheConsoleInfo ensures that the current console screen information has been queried
700 // since the last call to Flush(). It must be called before accessing h.curInfo or h.curPos.
701 func (h *windowsAnsiEventHandler) getCurrentInfo() (COORD, *CONSOLE_SCREEN_BUFFER_INFO, error) {
702         if h.curInfo == nil {
703                 info, err := GetConsoleScreenBufferInfo(h.fd)
704                 if err != nil {
705                         return COORD{}, nil, err
706                 }
707                 h.curInfo = info
708                 h.curPos = info.CursorPosition
709         }
710         return h.curPos, h.curInfo, nil
711 }
712
713 func (h *windowsAnsiEventHandler) updatePos(pos COORD) {
714         if h.curInfo == nil {
715                 panic("failed to call getCurrentInfo before calling updatePos")
716         }
717         h.curPos = pos
718 }
719
720 // clearWrap clears the state where the cursor is in the margin
721 // waiting for the next character before wrapping the line. This must
722 // be done before most operations that act on the cursor.
723 func (h *windowsAnsiEventHandler) clearWrap() {
724         h.wrapNext = false
725         h.drewMarginByte = false
726 }