windowchange.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #!/usr/bin/env python3
  2. # Interactive test program for assorted ancillary changes to the
  3. # terminal window - title, position, size, z-order, colour palette
  4. # etc.
  5. import argparse
  6. import sys
  7. import termios
  8. import os
  9. from time import sleep
  10. def send(s):
  11. sys.stdout.write(s)
  12. sys.stdout.flush()
  13. def query(s, lastchar=None):
  14. send(s)
  15. old_attrs = termios.tcgetattr(0)
  16. new_attrs = old_attrs.copy()
  17. new_attrs[3] &= ~(termios.ECHO | termios.ICANON)
  18. new_attrs[6][termios.VMIN] = 0
  19. new_attrs[6][termios.VTIME] = 1
  20. try:
  21. termios.tcsetattr(0, termios.TCSANOW, new_attrs)
  22. s = b""
  23. while True:
  24. c = os.read(0, 1)
  25. if len(c) == 0:
  26. break
  27. s += c
  28. if lastchar is not None and c[0] == lastchar:
  29. break
  30. return s
  31. finally:
  32. termios.tcsetattr(0, termios.TCSANOW, old_attrs)
  33. def pause(prompt):
  34. input(prompt + (" " if len(prompt) > 0 else "") + "--More--")
  35. def main():
  36. testnames = [
  37. "title",
  38. "icon",
  39. "minimise",
  40. "maximise",
  41. "minquery",
  42. "setpalette",
  43. "querypalette",
  44. "winpos",
  45. "setwinsize",
  46. "querywinsize",
  47. "zorder",
  48. "mousereport",
  49. ]
  50. parser = argparse.ArgumentParser(
  51. description='Test PuTTY\'s ancillary terminal updates')
  52. parser.add_argument("test", choices=testnames, nargs="*",
  53. help='Sub-test to perform (default: all of them)')
  54. args = parser.parse_args()
  55. if len(args.test) == 0:
  56. dotest = lambda s: True
  57. else:
  58. testset = set(args.test)
  59. dotest = lambda s: s in testset
  60. if dotest("title"):
  61. send("\033]2;Title test 1\a")
  62. pause("Title test 1.")
  63. send("\033]2;Title test 2\a")
  64. pause("Title test 2.")
  65. if dotest("icon"):
  66. send("\033]1;Icon-title test 1\a")
  67. pause("Icon-title test 1.")
  68. send("\033]1;Icon-title test 2\a")
  69. pause("Icon-title test 2.")
  70. if dotest("minimise"):
  71. pause("About to minimise and restore.")
  72. send("\033[2t")
  73. sleep(2)
  74. send("\033[1t")
  75. if dotest("maximise"):
  76. pause("About to maximise.")
  77. send("\033[9;1t")
  78. pause("About to unmaximise.")
  79. send("\033[9;0t")
  80. if dotest("minquery"):
  81. pause("Query minimised status.")
  82. s = query("\033[11t")
  83. if s == b"\033[1t":
  84. print("Reply: not minimised")
  85. elif s == b"\033[2t":
  86. print("Reply: minimised")
  87. else:
  88. print("Reply unknown:", repr(s))
  89. if dotest("setpalette"):
  90. print("Palette testing:")
  91. teststr = ""
  92. for i in range(8):
  93. teststr += "\033[0;{:d}m{:X}".format(i+30, i)
  94. for i in range(8):
  95. teststr += "\033[1;{:d}m{:X}".format(i+30, i+8)
  96. teststr += "\033[0;39mG\033[1mH\033[0;7mI\033[1mJ"
  97. teststr += "\033[m"
  98. teststr += " " * 9 + "K" + "\b" * 10
  99. for c in "0123456789ABCDEFGHIJKL":
  100. pause("Base: " + teststr)
  101. send("\033]P" + c + "ff0000")
  102. pause(c + " red: " + teststr)
  103. send("\033]P" + c + "00ff00")
  104. pause(c + " green: " + teststr)
  105. send("\033]P" + c + "0000ff")
  106. pause(c + " blue: " + teststr)
  107. send("\033]R")
  108. if dotest("querypalette"):
  109. send("\033]R")
  110. nfail = 262
  111. for i in range(nfail):
  112. s = query("\033]4;{:d};?\a".format(i), 7).decode("ASCII")
  113. prefix, suffix = "\033]4;{:d};rgb:".format(i), "\a"
  114. if s.startswith(prefix) and s.endswith(suffix):
  115. rgb = [int(word, 16) for word in
  116. s[len(prefix):-len(suffix)].split("/")]
  117. if 0 <= i < 8:
  118. j = i
  119. expected_rgb = [0xbbbb * ((j>>n) & 1) for n in range(3)]
  120. elif 0 <= i-8 < 8:
  121. j = i-8
  122. expected_rgb = [0x5555 + 0xaaaa * ((j>>n) & 1)
  123. for n in range(3)]
  124. elif 0 <= i-16 < 216:
  125. j = i-16
  126. expected_rgb = [(0 if v==0 else 0x3737+0x2828*v) for v in
  127. (j // 36, j // 6 % 6, j % 6)]
  128. elif 0 <= i-232 < 24:
  129. j = i-232
  130. expected_rgb = [j * 0x0a0a + 0x0808] * 3
  131. else:
  132. expected_rgb = [
  133. [0xbbbb, 0xbbbb, 0xbbbb], [0xffff, 0xffff, 0xffff],
  134. [0x0000, 0x0000, 0x0000], [0x5555, 0x5555, 0x5555],
  135. [0x0000, 0x0000, 0x0000], [0x0000, 0xffff, 0x0000],
  136. ][i-256]
  137. if expected_rgb == rgb:
  138. nfail -= 1
  139. else:
  140. print(i, "unexpected: {:04x} {:04x} {:04x}".format(*rgb))
  141. else:
  142. print(i, "bad format:", repr(s))
  143. print("Query palette: {:d} colours wrong".format(nfail))
  144. if dotest("winpos"):
  145. print("Query window position: ", end="")
  146. s = query("\033[13t").decode("ASCII")
  147. prefix, suffix = "\033[3;", "t"
  148. if s.startswith(prefix) and s.endswith(suffix):
  149. x, y = [int(word) for word in
  150. s[len(prefix):-len(suffix)].split(";")]
  151. print("x={:d} y={:d}".format(x, y))
  152. else:
  153. print("bad format:", repr(s))
  154. x, y = 50, 50
  155. pause("About to move window.")
  156. send("\033[3;{:d};{:d}t".format(x+50, y+50))
  157. pause("About to move it back again.")
  158. send("\033[3;{:d};{:d}t".format(x, y))
  159. if dotest("setwinsize"):
  160. pause("About to DECCOLM to 132 cols.")
  161. send("\033[?3h")
  162. pause("About to DECCOLM to 80 cols.")
  163. send("\033[?3l")
  164. pause("About to DECCOLM to 132 cols again.")
  165. send("\033[?3h")
  166. pause("About to reset the terminal.")
  167. send("\033c")
  168. pause("About to DECSLPP to 30 rows.")
  169. send("\033[30t")
  170. pause("About to DECSLPP to 24 rows.")
  171. send("\033[24t")
  172. pause("About to DECSNLS to 30 rows.")
  173. send("\033[*30|")
  174. pause("About to DECSNLS to 24 rows.")
  175. send("\033[*24|")
  176. pause("About to DECSCPP to 90 cols.")
  177. send("\033[$90|")
  178. pause("About to DECSCPP to 80 cols.")
  179. send("\033[$80|")
  180. pause("About to xterm to 90x30.")
  181. send("\033[8;30;90t")
  182. pause("About to xterm back to 80x24.")
  183. send("\033[8;24;80t")
  184. if dotest("querywinsize"):
  185. print("Query window size: ", end="")
  186. s = query("\033[14t").decode("ASCII")
  187. prefix, suffix = "\033[4;", "t"
  188. if s.startswith(prefix) and s.endswith(suffix):
  189. h, w = [int(word) for word in
  190. s[len(prefix):-len(suffix)].split(";")]
  191. print("w={:d} h={:d}".format(w, h))
  192. else:
  193. print("bad format:", repr(s))
  194. if dotest("zorder"):
  195. pause("About to lower to bottom and then raise.")
  196. send("\033[6t")
  197. sleep(2)
  198. send("\033[5t")
  199. if dotest("mousereport"):
  200. send("\033[?1000h")
  201. pause("Mouse reporting on: expect clicks to generate input")
  202. send("\033[?1000l")
  203. pause("Mouse reporting off: expect clicks to select")
  204. if __name__ == '__main__':
  205. main()