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.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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. self._light = 0
  40. def fore(self, fore=None, light=False, on_stderr=False):
  41. if fore is None:
  42. fore = self._default_fore
  43. self._fore = fore
  44. # Emulate LIGHT_EX with BRIGHT Style
  45. if light:
  46. self._light |= WinStyle.BRIGHT
  47. else:
  48. self._light &= ~WinStyle.BRIGHT
  49. self.set_console(on_stderr=on_stderr)
  50. def back(self, back=None, light=False, on_stderr=False):
  51. if back is None:
  52. back = self._default_back
  53. self._back = back
  54. # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style
  55. if light:
  56. self._light |= WinStyle.BRIGHT_BACKGROUND
  57. else:
  58. self._light &= ~WinStyle.BRIGHT_BACKGROUND
  59. self.set_console(on_stderr=on_stderr)
  60. def style(self, style=None, on_stderr=False):
  61. if style is None:
  62. style = self._default_style
  63. self._style = style
  64. self.set_console(on_stderr=on_stderr)
  65. def set_console(self, attrs=None, on_stderr=False):
  66. if attrs is None:
  67. attrs = self.get_attrs()
  68. handle = win32.STDOUT
  69. if on_stderr:
  70. handle = win32.STDERR
  71. win32.SetConsoleTextAttribute(handle, attrs)
  72. def get_position(self, handle):
  73. position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition
  74. # Because Windows coordinates are 0-based,
  75. # and win32.SetConsoleCursorPosition expects 1-based.
  76. position.X += 1
  77. position.Y += 1
  78. return position
  79. def set_cursor_position(self, position=None, on_stderr=False):
  80. if position is None:
  81. # I'm not currently tracking the position, so there is no default.
  82. # position = self.get_position()
  83. return
  84. handle = win32.STDOUT
  85. if on_stderr:
  86. handle = win32.STDERR
  87. win32.SetConsoleCursorPosition(handle, position)
  88. def cursor_adjust(self, x, y, on_stderr=False):
  89. handle = win32.STDOUT
  90. if on_stderr:
  91. handle = win32.STDERR
  92. position = self.get_position(handle)
  93. adjusted_position = (position.Y + y, position.X + x)
  94. win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False)
  95. def erase_screen(self, mode=0, on_stderr=False):
  96. # 0 should clear from the cursor to the end of the screen.
  97. # 1 should clear from the cursor to the beginning of the screen.
  98. # 2 should clear the entire screen, and move cursor to (1,1)
  99. handle = win32.STDOUT
  100. if on_stderr:
  101. handle = win32.STDERR
  102. csbi = win32.GetConsoleScreenBufferInfo(handle)
  103. # get the number of character cells in the current buffer
  104. cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y
  105. # get number of character cells before current cursor position
  106. cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X
  107. if mode == 0:
  108. from_coord = csbi.dwCursorPosition
  109. cells_to_erase = cells_in_screen - cells_before_cursor
  110. elif mode == 1:
  111. from_coord = win32.COORD(0, 0)
  112. cells_to_erase = cells_before_cursor
  113. elif mode == 2:
  114. from_coord = win32.COORD(0, 0)
  115. cells_to_erase = cells_in_screen
  116. else:
  117. # invalid mode
  118. return
  119. # fill the entire screen with blanks
  120. win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
  121. # now set the buffer's attributes accordingly
  122. win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
  123. if mode == 2:
  124. # put the cursor where needed
  125. win32.SetConsoleCursorPosition(handle, (1, 1))
  126. def erase_line(self, mode=0, on_stderr=False):
  127. # 0 should clear from the cursor to the end of the line.
  128. # 1 should clear from the cursor to the beginning of the line.
  129. # 2 should clear the entire line.
  130. handle = win32.STDOUT
  131. if on_stderr:
  132. handle = win32.STDERR
  133. csbi = win32.GetConsoleScreenBufferInfo(handle)
  134. if mode == 0:
  135. from_coord = csbi.dwCursorPosition
  136. cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X
  137. elif mode == 1:
  138. from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
  139. cells_to_erase = csbi.dwCursorPosition.X
  140. elif mode == 2:
  141. from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
  142. cells_to_erase = csbi.dwSize.X
  143. else:
  144. # invalid mode
  145. return
  146. # fill the entire screen with blanks
  147. win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
  148. # now set the buffer's attributes accordingly
  149. win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
  150. def set_title(self, title):
  151. win32.SetConsoleTitle(title)