win_event_handler.go 17 KB

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