You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

winterm.py 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
  2. from . import win32
  3. # from wincon.h
  4. class WinColor(object):
  5. BLACK = 0
  6. BLUE = 1
  7. GREEN = 2
  8. CYAN = 3
  9. RED = 4
  10. MAGENTA = 5
  11. YELLOW = 6
  12. GREY = 7
  13. # from wincon.h
  14. class WinStyle(object):
  15. NORMAL = 0x00 # dim text, dim background
  16. BRIGHT = 0x08 # bright text, dim background
  17. BRIGHT_BACKGROUND = 0x80 # dim text, bright background
  18. class WinTerm(object):
  19. def __init__(self):
  20. self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes
  21. self.set_attrs(self._default)
  22. self._default_fore = self._fore
  23. self._default_back = self._back
  24. self._default_style = self._style
  25. # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style.
  26. # So that LIGHT_EX colors and BRIGHT style do not clobber each other,
  27. # we track them separately, since LIGHT_EX is overwritten by Fore/Back
  28. # and BRIGHT is overwritten by Style codes.
  29. self._light = 0
  30. def get_attrs(self):
  31. return self._fore + self._back * 16 + (self._style | self._light)
  32. def set_attrs(self, value):
  33. self._fore = value & 7
  34. self._back = (value >> 4) & 7
  35. self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND)
  36. def reset_all(self, on_stderr=None):
  37. self.set_attrs(self._default)
  38. self.set_console(attrs=self._default)
  39. def fore(self, fore=None, light=False, on_stderr=False):
  40. if fore is None:
  41. fore = self._default_fore
  42. self._fore = fore
  43. # Emulate LIGHT_EX with BRIGHT Style
  44. if light:
  45. self._light |= WinStyle.BRIGHT
  46. else:
  47. self._light &= ~WinStyle.BRIGHT
  48. self.set_console(on_stderr=on_stderr)
  49. def back(self, back=None, light=False, on_stderr=False):
  50. if back is None:
  51. back = self._default_back
  52. self._back = back
  53. # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style
  54. if light:
  55. self._light |= WinStyle.BRIGHT_BACKGROUND
  56. else:
  57. self._light &= ~WinStyle.BRIGHT_BACKGROUND
  58. self.set_console(on_stderr=on_stderr)
  59. def style(self, style=None, on_stderr=False):
  60. if style is None:
  61. style = self._default_style
  62. self._style = style
  63. self.set_console(on_stderr=on_stderr)
  64. def set_console(self, attrs=None, on_stderr=False):
  65. if attrs is None:
  66. attrs = self.get_attrs()
  67. handle = win32.STDOUT
  68. if on_stderr:
  69. handle = win32.STDERR
  70. win32.SetConsoleTextAttribute(handle, attrs)
  71. def get_position(self, handle):
  72. position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition
  73. # Because Windows coordinates are 0-based,
  74. # and win32.SetConsoleCursorPosition expects 1-based.
  75. position.X += 1
  76. position.Y += 1
  77. return position
  78. def set_cursor_position(self, position=None, on_stderr=False):
  79. if position is None:
  80. # I'm not currently tracking the position, so there is no default.
  81. # position = self.get_position()
  82. return
  83. handle = win32.STDOUT
  84. if on_stderr:
  85. handle = win32.STDERR
  86. win32.SetConsoleCursorPosition(handle, position)
  87. def cursor_adjust(self, x, y, on_stderr=False):
  88. handle = win32.STDOUT
  89. if on_stderr:
  90. handle = win32.STDERR
  91. position = self.get_position(handle)
  92. adjusted_position = (position.Y + y, position.X + x)
  93. win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False)
  94. def erase_screen(self, mode=0, on_stderr=False):
  95. # 0 should clear from the cursor to the end of the screen.
  96. # 1 should clear from the cursor to the beginning of the screen.
  97. # 2 should clear the entire screen, and move cursor to (1,1)
  98. handle = win32.STDOUT
  99. if on_stderr:
  100. handle = win32.STDERR
  101. csbi = win32.GetConsoleScreenBufferInfo(handle)
  102. # get the number of character cells in the current buffer
  103. cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y
  104. # get number of character cells before current cursor position
  105. cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X
  106. if mode == 0:
  107. from_coord = csbi.dwCursorPosition
  108. cells_to_erase = cells_in_screen - cells_before_cursor
  109. if mode == 1:
  110. from_coord = win32.COORD(0, 0)
  111. cells_to_erase = cells_before_cursor
  112. elif mode == 2:
  113. from_coord = win32.COORD(0, 0)
  114. cells_to_erase = cells_in_screen
  115. # fill the entire screen with blanks
  116. win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
  117. # now set the buffer's attributes accordingly
  118. win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
  119. if mode == 2:
  120. # put the cursor where needed
  121. win32.SetConsoleCursorPosition(handle, (1, 1))
  122. def erase_line(self, mode=0, on_stderr=False):
  123. # 0 should clear from the cursor to the end of the line.
  124. # 1 should clear from the cursor to the beginning of the line.
  125. # 2 should clear the entire line.
  126. handle = win32.STDOUT
  127. if on_stderr:
  128. handle = win32.STDERR
  129. csbi = win32.GetConsoleScreenBufferInfo(handle)
  130. if mode == 0:
  131. from_coord = csbi.dwCursorPosition
  132. cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X
  133. if mode == 1:
  134. from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
  135. cells_to_erase = csbi.dwCursorPosition.X
  136. elif mode == 2:
  137. from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
  138. cells_to_erase = csbi.dwSize.X
  139. # fill the entire screen with blanks
  140. win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
  141. # now set the buffer's attributes accordingly
  142. win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
  143. def set_title(self, title):
  144. win32.SetConsoleTitle(title)