...

Source file src/github.com/Azure/go-ansiterm/winterm/win_event_handler.go

Documentation: github.com/Azure/go-ansiterm/winterm

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

View as plain text