m2m模型翻译
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.

824 lines
30 KiB

6 months ago
  1. # -*- coding: utf-8 -*-
  2. #*****************************************************************************
  3. # Copyright (C) 2003-2006 Gary Bishop.
  4. # Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
  5. #
  6. # Distributed under the terms of the BSD License. The full license is in
  7. # the file COPYING, distributed as part of this software.
  8. #*****************************************************************************
  9. from __future__ import print_function, unicode_literals, absolute_import
  10. '''Cursor control and color for the Windows console.
  11. This was modeled after the C extension of the same name by Fredrik Lundh.
  12. '''
  13. # primitive debug printing that won't interfere with the screen
  14. import sys,os
  15. import traceback
  16. import re
  17. import pyreadline.unicode_helper as unicode_helper
  18. from pyreadline.logger import log
  19. from pyreadline.unicode_helper import ensure_unicode, ensure_str
  20. from pyreadline.keysyms import make_KeyPress, KeyPress
  21. from pyreadline.console.ansi import AnsiState, AnsiWriter
  22. try:
  23. import ctypes.util
  24. from ctypes import *
  25. from _ctypes import call_function
  26. from ctypes.wintypes import *
  27. except ImportError:
  28. raise ImportError("You need ctypes to run this code")
  29. if sys.version_info < (2, 6):
  30. bytes = str
  31. def nolog(string):
  32. pass
  33. log = nolog
  34. # some constants we need
  35. STD_INPUT_HANDLE = -10
  36. STD_OUTPUT_HANDLE = -11
  37. ENABLE_WINDOW_INPUT = 0x0008
  38. ENABLE_MOUSE_INPUT = 0x0010
  39. ENABLE_PROCESSED_INPUT = 0x0001
  40. WHITE = 0x7
  41. BLACK = 0
  42. MENU_EVENT = 0x0008
  43. KEY_EVENT = 0x0001
  44. MOUSE_MOVED = 0x0001
  45. MOUSE_EVENT = 0x0002
  46. WINDOW_BUFFER_SIZE_EVENT = 0x0004
  47. FOCUS_EVENT = 0x0010
  48. MENU_EVENT = 0x0008
  49. VK_SHIFT = 0x10
  50. VK_CONTROL = 0x11
  51. VK_MENU = 0x12
  52. GENERIC_READ = int(0x80000000)
  53. GENERIC_WRITE = 0x40000000
  54. # Windows structures we'll need later
  55. class COORD(Structure):
  56. _fields_ = [("X", c_short),
  57. ("Y", c_short)]
  58. class SMALL_RECT(Structure):
  59. _fields_ = [("Left", c_short),
  60. ("Top", c_short),
  61. ("Right", c_short),
  62. ("Bottom", c_short)]
  63. class CONSOLE_SCREEN_BUFFER_INFO(Structure):
  64. _fields_ = [("dwSize", COORD),
  65. ("dwCursorPosition", COORD),
  66. ("wAttributes", c_short),
  67. ("srWindow", SMALL_RECT),
  68. ("dwMaximumWindowSize", COORD)]
  69. class CHAR_UNION(Union):
  70. _fields_ = [("UnicodeChar", c_wchar),
  71. ("AsciiChar", c_char)]
  72. class CHAR_INFO(Structure):
  73. _fields_ = [("Char", CHAR_UNION),
  74. ("Attributes", c_short)]
  75. class KEY_EVENT_RECORD(Structure):
  76. _fields_ = [("bKeyDown", c_byte),
  77. ("pad2", c_byte),
  78. ('pad1', c_short),
  79. ("wRepeatCount", c_short),
  80. ("wVirtualKeyCode", c_short),
  81. ("wVirtualScanCode", c_short),
  82. ("uChar", CHAR_UNION),
  83. ("dwControlKeyState", c_int)]
  84. class MOUSE_EVENT_RECORD(Structure):
  85. _fields_ = [("dwMousePosition", COORD),
  86. ("dwButtonState", c_int),
  87. ("dwControlKeyState", c_int),
  88. ("dwEventFlags", c_int)]
  89. class WINDOW_BUFFER_SIZE_RECORD(Structure):
  90. _fields_ = [("dwSize", COORD)]
  91. class MENU_EVENT_RECORD(Structure):
  92. _fields_ = [("dwCommandId", c_uint)]
  93. class FOCUS_EVENT_RECORD(Structure):
  94. _fields_ = [("bSetFocus", c_byte)]
  95. class INPUT_UNION(Union):
  96. _fields_ = [("KeyEvent", KEY_EVENT_RECORD),
  97. ("MouseEvent", MOUSE_EVENT_RECORD),
  98. ("WindowBufferSizeEvent", WINDOW_BUFFER_SIZE_RECORD),
  99. ("MenuEvent", MENU_EVENT_RECORD),
  100. ("FocusEvent", FOCUS_EVENT_RECORD)]
  101. class INPUT_RECORD(Structure):
  102. _fields_ = [("EventType", c_short),
  103. ("Event", INPUT_UNION)]
  104. class CONSOLE_CURSOR_INFO(Structure):
  105. _fields_ = [("dwSize", c_int),
  106. ("bVisible", c_byte)]
  107. # I didn't want to have to individually import these so I made a list, they are
  108. # added to the Console class later in this file.
  109. funcs = [
  110. 'AllocConsole',
  111. 'CreateConsoleScreenBuffer',
  112. 'FillConsoleOutputAttribute',
  113. 'FillConsoleOutputCharacterW',
  114. 'FreeConsole',
  115. 'GetConsoleCursorInfo',
  116. 'GetConsoleMode',
  117. 'GetConsoleScreenBufferInfo',
  118. 'GetConsoleTitleW',
  119. 'GetProcAddress',
  120. 'GetStdHandle',
  121. 'PeekConsoleInputW',
  122. 'ReadConsoleInputW',
  123. 'ScrollConsoleScreenBufferW',
  124. 'SetConsoleActiveScreenBuffer',
  125. 'SetConsoleCursorInfo',
  126. 'SetConsoleCursorPosition',
  127. 'SetConsoleMode',
  128. 'SetConsoleScreenBufferSize',
  129. 'SetConsoleTextAttribute',
  130. 'SetConsoleTitleW',
  131. 'SetConsoleWindowInfo',
  132. 'WriteConsoleW',
  133. 'WriteConsoleOutputCharacterW',
  134. 'WriteFile',
  135. ]
  136. # I don't want events for these keys, they are just a bother for my application
  137. key_modifiers = { VK_SHIFT : 1,
  138. VK_CONTROL : 1,
  139. VK_MENU : 1, # alt key
  140. 0x5b : 1, # windows key
  141. }
  142. def split_block(text, size=1000):
  143. return [text[start:start + size] for start in range(0, len(text), size)]
  144. class Console(object):
  145. '''Console driver for Windows.
  146. '''
  147. def __init__(self, newbuffer=0):
  148. '''Initialize the Console object.
  149. newbuffer=1 will allocate a new buffer so the old content will be restored
  150. on exit.
  151. '''
  152. #Do I need the following line? It causes a console to be created whenever
  153. #readline is imported into a pythonw application which seems wrong. Things
  154. #seem to work without it...
  155. #self.AllocConsole()
  156. if newbuffer:
  157. self.hout = self.CreateConsoleScreenBuffer(
  158. GENERIC_READ | GENERIC_WRITE,
  159. 0, None, 1, None)
  160. self.SetConsoleActiveScreenBuffer(self.hout)
  161. else:
  162. self.hout = self.GetStdHandle(STD_OUTPUT_HANDLE)
  163. self.hin = self.GetStdHandle(STD_INPUT_HANDLE)
  164. self.inmode = DWORD(0)
  165. self.GetConsoleMode(self.hin, byref(self.inmode))
  166. self.SetConsoleMode(self.hin, 0xf)
  167. info = CONSOLE_SCREEN_BUFFER_INFO()
  168. self.GetConsoleScreenBufferInfo(self.hout, byref(info))
  169. self.attr = info.wAttributes
  170. self.saveattr = info.wAttributes # remember the initial colors
  171. self.defaultstate = AnsiState()
  172. self.defaultstate.winattr = info.wAttributes
  173. self.ansiwriter = AnsiWriter(self.defaultstate)
  174. background = self.attr & 0xf0
  175. for escape in self.escape_to_color:
  176. if self.escape_to_color[escape] is not None:
  177. self.escape_to_color[escape] |= background
  178. log('initial attr=%x' % self.attr)
  179. self.softspace = 0 # this is for using it as a file-like object
  180. self.serial = 0
  181. self.pythondll = ctypes.pythonapi
  182. self.inputHookPtr = \
  183. c_void_p.from_address(addressof(self.pythondll.PyOS_InputHook)).value
  184. if sys.version_info[:2] > (3, 4):
  185. self.pythondll.PyMem_RawMalloc.restype = c_size_t
  186. self.pythondll.PyMem_Malloc.argtypes = [c_size_t]
  187. setattr(Console, 'PyMem_Malloc', self.pythondll.PyMem_RawMalloc)
  188. else:
  189. self.pythondll.PyMem_Malloc.restype = c_size_t
  190. self.pythondll.PyMem_Malloc.argtypes = [c_size_t]
  191. setattr(Console, 'PyMem_Malloc', self.pythondll.PyMem_Malloc)
  192. def __del__(self):
  193. '''Cleanup the console when finished.'''
  194. # I don't think this ever gets called
  195. self.SetConsoleTextAttribute(self.hout, self.saveattr)
  196. self.SetConsoleMode(self.hin, self.inmode)
  197. self.FreeConsole()
  198. def _get_top_bot(self):
  199. info = CONSOLE_SCREEN_BUFFER_INFO()
  200. self.GetConsoleScreenBufferInfo(self.hout, byref(info))
  201. rect = info.srWindow
  202. top = rect.Top
  203. bot = rect.Bottom
  204. return top,bot
  205. def fixcoord(self, x, y):
  206. '''Return a long with x and y packed inside,
  207. also handle negative x and y.'''
  208. if x < 0 or y < 0:
  209. info = CONSOLE_SCREEN_BUFFER_INFO()
  210. self.GetConsoleScreenBufferInfo(self.hout, byref(info))
  211. if x < 0:
  212. x = info.srWindow.Right - x
  213. y = info.srWindow.Bottom + y
  214. # this is a hack! ctypes won't pass structures but COORD is
  215. # just like a long, so this works.
  216. return c_int(y << 16 | x)
  217. def pos(self, x=None, y=None):
  218. '''Move or query the window cursor.'''
  219. if x is None:
  220. info = CONSOLE_SCREEN_BUFFER_INFO()
  221. self.GetConsoleScreenBufferInfo(self.hout, byref(info))
  222. return (info.dwCursorPosition.X, info.dwCursorPosition.Y)
  223. else:
  224. return self.SetConsoleCursorPosition(self.hout,
  225. self.fixcoord(x, y))
  226. def home(self):
  227. '''Move to home.'''
  228. self.pos(0, 0)
  229. # Map ANSI color escape sequences into Windows Console Attributes
  230. terminal_escape = re.compile('(\001?\033\\[[0-9;]+m\002?)')
  231. escape_parts = re.compile('\001?\033\\[([0-9;]+)m\002?')
  232. escape_to_color = { '0;30': 0x0, #black
  233. '0;31': 0x4, #red
  234. '0;32': 0x2, #green
  235. '0;33': 0x4+0x2, #brown?
  236. '0;34': 0x1, #blue
  237. '0;35': 0x1+0x4, #purple
  238. '0;36': 0x2+0x4, #cyan
  239. '0;37': 0x1+0x2+0x4, #grey
  240. '1;30': 0x1+0x2+0x4, #dark gray
  241. '1;31': 0x4+0x8, #red
  242. '1;32': 0x2+0x8, #light green
  243. '1;33': 0x4+0x2+0x8, #yellow
  244. '1;34': 0x1+0x8, #light blue
  245. '1;35': 0x1+0x4+0x8, #light purple
  246. '1;36': 0x1+0x2+0x8, #light cyan
  247. '1;37': 0x1+0x2+0x4+0x8, #white
  248. '0': None,
  249. }
  250. # This pattern should match all characters that change the cursor position differently
  251. # than a normal character.
  252. motion_char_re = re.compile('([\n\r\t\010\007])')
  253. def write_scrolling(self, text, attr=None):
  254. '''write text at current cursor position while watching for scrolling.
  255. If the window scrolls because you are at the bottom of the screen
  256. buffer, all positions that you are storing will be shifted by the
  257. scroll amount. For example, I remember the cursor position of the
  258. prompt so that I can redraw the line but if the window scrolls,
  259. the remembered position is off.
  260. This variant of write tries to keep track of the cursor position
  261. so that it will know when the screen buffer is scrolled. It
  262. returns the number of lines that the buffer scrolled.
  263. '''
  264. text = ensure_unicode(text)
  265. x, y = self.pos()
  266. w, h = self.size()
  267. scroll = 0 # the result
  268. # split the string into ordinary characters and funny characters
  269. chunks = self.motion_char_re.split(text)
  270. for chunk in chunks:
  271. n = self.write_color(chunk, attr)
  272. if len(chunk) == 1: # the funny characters will be alone
  273. if chunk[0] == '\n': # newline
  274. x = 0
  275. y += 1
  276. elif chunk[0] == '\r': # carriage return
  277. x = 0
  278. elif chunk[0] == '\t': # tab
  279. x = 8 * (int(x / 8) + 1)
  280. if x > w: # newline
  281. x -= w
  282. y += 1
  283. elif chunk[0] == '\007': # bell
  284. pass
  285. elif chunk[0] == '\010':
  286. x -= 1
  287. if x < 0:
  288. y -= 1 # backed up 1 line
  289. else: # ordinary character
  290. x += 1
  291. if x == w: # wrap
  292. x = 0
  293. y += 1
  294. if y == h: # scroll
  295. scroll += 1
  296. y = h - 1
  297. else: # chunk of ordinary characters
  298. x += n
  299. l = int(x / w) # lines we advanced
  300. x = x % w # new x value
  301. y += l
  302. if y >= h: # scroll
  303. scroll += y - h + 1
  304. y = h - 1
  305. return scroll
  306. def write_color(self, text, attr=None):
  307. text = ensure_unicode(text)
  308. n, res= self.ansiwriter.write_color(text, attr)
  309. junk = DWORD(0)
  310. for attr,chunk in res:
  311. log("console.attr:%s"%(attr))
  312. log("console.chunk:%s"%(chunk))
  313. self.SetConsoleTextAttribute(self.hout, attr.winattr)
  314. for short_chunk in split_block(chunk):
  315. self.WriteConsoleW(self.hout, short_chunk,
  316. len(short_chunk), byref(junk), None)
  317. return n
  318. def write_plain(self, text, attr=None):
  319. '''write text at current cursor position.'''
  320. text = ensure_unicode(text)
  321. log('write("%s", %s)' %(text, attr))
  322. if attr is None:
  323. attr = self.attr
  324. junk = DWORD(0)
  325. self.SetConsoleTextAttribute(self.hout, attr)
  326. for short_chunk in split_block(chunk):
  327. self.WriteConsoleW(self.hout, ensure_unicode(short_chunk),
  328. len(short_chunk), byref(junk), None)
  329. return len(text)
  330. #This function must be used to ensure functioning with EMACS
  331. #Emacs sets the EMACS environment variable
  332. if "EMACS" in os.environ:
  333. def write_color(self, text, attr=None):
  334. text = ensure_str(text)
  335. junk = DWORD(0)
  336. self.WriteFile(self.hout, text, len(text), byref(junk), None)
  337. return len(text)
  338. write_plain = write_color
  339. # make this class look like a file object
  340. def write(self, text):
  341. text = ensure_unicode(text)
  342. log('write("%s")' % text)
  343. return self.write_color(text)
  344. #write = write_scrolling
  345. def isatty(self):
  346. return True
  347. def flush(self):
  348. pass
  349. def page(self, attr=None, fill=' '):
  350. '''Fill the entire screen.'''
  351. if attr is None:
  352. attr = self.attr
  353. if len(fill) != 1:
  354. raise ValueError
  355. info = CONSOLE_SCREEN_BUFFER_INFO()
  356. self.GetConsoleScreenBufferInfo(self.hout, byref(info))
  357. if info.dwCursorPosition.X != 0 or info.dwCursorPosition.Y != 0:
  358. self.SetConsoleCursorPosition(self.hout, self.fixcoord(0, 0))
  359. w = info.dwSize.X
  360. n = DWORD(0)
  361. for y in range(info.dwSize.Y):
  362. self.FillConsoleOutputAttribute(self.hout, attr,
  363. w, self.fixcoord(0, y), byref(n))
  364. self.FillConsoleOutputCharacterW(self.hout, ord(fill[0]),
  365. w, self.fixcoord(0, y), byref(n))
  366. self.attr = attr
  367. def text(self, x, y, text, attr=None):
  368. '''Write text at the given position.'''
  369. if attr is None:
  370. attr = self.attr
  371. pos = self.fixcoord(x, y)
  372. n = DWORD(0)
  373. self.WriteConsoleOutputCharacterW(self.hout, text,
  374. len(text), pos, byref(n))
  375. self.FillConsoleOutputAttribute(self.hout, attr, n, pos, byref(n))
  376. def clear_to_end_of_window(self):
  377. top, bot = self._get_top_bot()
  378. pos = self.pos()
  379. w, h = self.size()
  380. self.rectangle( (pos[0], pos[1], w, pos[1] + 1))
  381. if pos[1] < bot:
  382. self.rectangle((0, pos[1] + 1, w, bot + 1))
  383. def rectangle(self, rect, attr=None, fill=' '):
  384. '''Fill Rectangle.'''
  385. x0, y0, x1, y1 = rect
  386. n = DWORD(0)
  387. if attr is None:
  388. attr = self.attr
  389. for y in range(y0, y1):
  390. pos = self.fixcoord(x0, y)
  391. self.FillConsoleOutputAttribute(self.hout, attr, x1 - x0,
  392. pos, byref(n))
  393. self.FillConsoleOutputCharacterW(self.hout, ord(fill[0]), x1 - x0,
  394. pos, byref(n))
  395. def scroll(self, rect, dx, dy, attr=None, fill=' '):
  396. '''Scroll a rectangle.'''
  397. if attr is None:
  398. attr = self.attr
  399. x0, y0, x1, y1 = rect
  400. source = SMALL_RECT(x0, y0, x1 - 1, y1 - 1)
  401. dest = self.fixcoord(x0 + dx, y0 + dy)
  402. style = CHAR_INFO()
  403. style.Char.AsciiChar = ensure_str(fill[0])
  404. style.Attributes = attr
  405. return self.ScrollConsoleScreenBufferW(self.hout, byref(source),
  406. byref(source), dest, byref(style))
  407. def scroll_window(self, lines):
  408. '''Scroll the window by the indicated number of lines.'''
  409. info = CONSOLE_SCREEN_BUFFER_INFO()
  410. self.GetConsoleScreenBufferInfo(self.hout, byref(info))
  411. rect = info.srWindow
  412. log('sw: rtop=%d rbot=%d' % (rect.Top, rect.Bottom))
  413. top = rect.Top + lines
  414. bot = rect.Bottom + lines
  415. h = bot - top
  416. maxbot = info.dwSize.Y-1
  417. if top < 0:
  418. top = 0
  419. bot = h
  420. if bot > maxbot:
  421. bot = maxbot
  422. top = bot - h
  423. nrect = SMALL_RECT()
  424. nrect.Top = top
  425. nrect.Bottom = bot
  426. nrect.Left = rect.Left
  427. nrect.Right = rect.Right
  428. log('sn: top=%d bot=%d' % (top, bot))
  429. r=self.SetConsoleWindowInfo(self.hout, True, byref(nrect))
  430. log('r=%d' % r)
  431. def get(self):
  432. '''Get next event from queue.'''
  433. inputHookFunc = c_void_p.from_address(self.inputHookPtr).value
  434. Cevent = INPUT_RECORD()
  435. count = DWORD(0)
  436. while 1:
  437. if inputHookFunc:
  438. call_function(inputHookFunc, ())
  439. status = self.ReadConsoleInputW(self.hin,
  440. byref(Cevent), 1, byref(count))
  441. if status and count.value == 1:
  442. e = event(self, Cevent)
  443. return e
  444. def getkeypress(self):
  445. '''Return next key press event from the queue, ignoring others.'''
  446. while 1:
  447. e = self.get()
  448. if e.type == 'KeyPress' and e.keycode not in key_modifiers:
  449. log("console.getkeypress %s"%e)
  450. if e.keyinfo.keyname == 'next':
  451. self.scroll_window(12)
  452. elif e.keyinfo.keyname == 'prior':
  453. self.scroll_window(-12)
  454. else:
  455. return e
  456. elif ((e.type == 'KeyRelease') and
  457. (e.keyinfo == KeyPress('S', False, True, False, 'S'))):
  458. log("getKeypress:%s,%s,%s"%(e.keyinfo, e.keycode, e.type))
  459. return e
  460. def getchar(self):
  461. '''Get next character from queue.'''
  462. Cevent = INPUT_RECORD()
  463. count = DWORD(0)
  464. while 1:
  465. status = self.ReadConsoleInputW(self.hin,
  466. byref(Cevent), 1, byref(count))
  467. if (status and
  468. (count.value == 1) and
  469. (Cevent.EventType == 1) and
  470. Cevent.Event.KeyEvent.bKeyDown):
  471. sym = keysym(Cevent.Event.KeyEvent.wVirtualKeyCode)
  472. if len(sym) == 0:
  473. sym = Cevent.Event.KeyEvent.uChar.AsciiChar
  474. return sym
  475. def peek(self):
  476. '''Check event queue.'''
  477. Cevent = INPUT_RECORD()
  478. count = DWORD(0)
  479. status = self.PeekConsoleInputW(self.hin,
  480. byref(Cevent), 1, byref(count))
  481. if status and count == 1:
  482. return event(self, Cevent)
  483. def title(self, txt=None):
  484. '''Set/get title.'''
  485. if txt:
  486. self.SetConsoleTitleW(txt)
  487. else:
  488. buffer = create_unicode_buffer(200)
  489. n = self.GetConsoleTitleW(buffer, 200)
  490. if n > 0:
  491. return buffer.value[:n]
  492. def size(self, width=None, height=None):
  493. '''Set/get window size.'''
  494. info = CONSOLE_SCREEN_BUFFER_INFO()
  495. status = self.GetConsoleScreenBufferInfo(self.hout, byref(info))
  496. if not status:
  497. return None
  498. if width is not None and height is not None:
  499. wmin = info.srWindow.Right - info.srWindow.Left + 1
  500. hmin = info.srWindow.Bottom - info.srWindow.Top + 1
  501. #print wmin, hmin
  502. width = max(width, wmin)
  503. height = max(height, hmin)
  504. #print width, height
  505. self.SetConsoleScreenBufferSize(self.hout,
  506. self.fixcoord(width, height))
  507. else:
  508. return (info.dwSize.X, info.dwSize.Y)
  509. def cursor(self, visible=None, size=None):
  510. '''Set cursor on or off.'''
  511. info = CONSOLE_CURSOR_INFO()
  512. if self.GetConsoleCursorInfo(self.hout, byref(info)):
  513. if visible is not None:
  514. info.bVisible = visible
  515. if size is not None:
  516. info.dwSize = size
  517. self.SetConsoleCursorInfo(self.hout, byref(info))
  518. def bell(self):
  519. self.write('\007')
  520. def next_serial(self):
  521. '''Get next event serial number.'''
  522. self.serial += 1
  523. return self.serial
  524. # add the functions from the dll to the class
  525. for func in funcs:
  526. setattr(Console, func, getattr(windll.kernel32, func))
  527. _strncpy = ctypes.windll.kernel32.lstrcpynA
  528. _strncpy.restype = c_char_p
  529. _strncpy.argtypes = [c_char_p, c_char_p, c_size_t]
  530. LPVOID = c_void_p
  531. LPCVOID = c_void_p
  532. FARPROC = c_void_p
  533. LPDWORD = POINTER(DWORD)
  534. Console.AllocConsole.restype = BOOL
  535. Console.AllocConsole.argtypes = [] #void
  536. Console.CreateConsoleScreenBuffer.restype = HANDLE
  537. Console.CreateConsoleScreenBuffer.argtypes = [DWORD, DWORD, c_void_p, DWORD, LPVOID] #DWORD, DWORD, SECURITY_ATTRIBUTES*, DWORD, LPVOID
  538. Console.FillConsoleOutputAttribute.restype = BOOL
  539. Console.FillConsoleOutputAttribute.argtypes = [HANDLE, WORD, DWORD, c_int, LPDWORD] #HANDLE, WORD, DWORD, COORD, LPDWORD
  540. Console.FillConsoleOutputCharacterW.restype = BOOL
  541. Console.FillConsoleOutputCharacterW.argtypes = [HANDLE, c_ushort, DWORD, c_int, LPDWORD] #HANDLE, TCHAR, DWORD, COORD, LPDWORD
  542. Console.FreeConsole.restype = BOOL
  543. Console.FreeConsole.argtypes = [] #void
  544. Console.GetConsoleCursorInfo.restype = BOOL
  545. Console.GetConsoleCursorInfo.argtypes = [HANDLE, c_void_p] #HANDLE, PCONSOLE_CURSOR_INFO
  546. Console.GetConsoleMode.restype = BOOL
  547. Console.GetConsoleMode.argtypes = [HANDLE, LPDWORD] #HANDLE, LPDWORD
  548. Console.GetConsoleScreenBufferInfo.restype = BOOL
  549. Console.GetConsoleScreenBufferInfo.argtypes = [HANDLE, c_void_p] #HANDLE, PCONSOLE_SCREEN_BUFFER_INFO
  550. Console.GetConsoleTitleW.restype = DWORD
  551. Console.GetConsoleTitleW.argtypes = [c_wchar_p, DWORD] #LPTSTR , DWORD
  552. Console.GetProcAddress.restype = FARPROC
  553. Console.GetProcAddress.argtypes = [HMODULE, c_char_p] #HMODULE , LPCSTR
  554. Console.GetStdHandle.restype = HANDLE
  555. Console.GetStdHandle.argtypes = [DWORD]
  556. Console.PeekConsoleInputW.restype = BOOL
  557. Console.PeekConsoleInputW.argtypes = [HANDLE, c_void_p, DWORD, LPDWORD] #HANDLE, PINPUT_RECORD, DWORD, LPDWORD
  558. Console.ReadConsoleInputW.restype = BOOL
  559. Console.ReadConsoleInputW.argtypes = [HANDLE, c_void_p, DWORD, LPDWORD] #HANDLE, PINPUT_RECORD, DWORD, LPDWORD
  560. Console.ScrollConsoleScreenBufferW.restype = BOOL
  561. Console.ScrollConsoleScreenBufferW.argtypes = [HANDLE, c_void_p, c_void_p, c_int, c_void_p] #HANDLE, SMALL_RECT*, SMALL_RECT*, COORD, LPDWORD
  562. Console.SetConsoleActiveScreenBuffer.restype = BOOL
  563. Console.SetConsoleActiveScreenBuffer.argtypes = [HANDLE] #HANDLE
  564. Console.SetConsoleCursorInfo.restype = BOOL
  565. Console.SetConsoleCursorInfo.argtypes = [HANDLE, c_void_p] #HANDLE, CONSOLE_CURSOR_INFO*
  566. Console.SetConsoleCursorPosition.restype = BOOL
  567. Console.SetConsoleCursorPosition.argtypes = [HANDLE, c_int] #HANDLE, COORD
  568. Console.SetConsoleMode.restype = BOOL
  569. Console.SetConsoleMode.argtypes = [HANDLE, DWORD] #HANDLE, DWORD
  570. Console.SetConsoleScreenBufferSize.restype = BOOL
  571. Console.SetConsoleScreenBufferSize.argtypes = [HANDLE, c_int] #HANDLE, COORD
  572. Console.SetConsoleTextAttribute.restype = BOOL
  573. Console.SetConsoleTextAttribute.argtypes = [HANDLE, WORD] #HANDLE, WORD
  574. Console.SetConsoleTitleW.restype = BOOL
  575. Console.SetConsoleTitleW.argtypes = [c_wchar_p] #LPCTSTR
  576. Console.SetConsoleWindowInfo.restype = BOOL
  577. Console.SetConsoleWindowInfo.argtypes = [HANDLE, BOOL, c_void_p] #HANDLE, BOOL, SMALL_RECT*
  578. Console.WriteConsoleW.restype = BOOL
  579. Console.WriteConsoleW.argtypes = [HANDLE, c_void_p, DWORD, LPDWORD, LPVOID] #HANDLE, VOID*, DWORD, LPDWORD, LPVOID
  580. Console.WriteConsoleOutputCharacterW.restype = BOOL
  581. Console.WriteConsoleOutputCharacterW.argtypes = [HANDLE, c_wchar_p, DWORD, c_int, LPDWORD] #HANDLE, LPCTSTR, DWORD, COORD, LPDWORD
  582. Console.WriteFile.restype = BOOL
  583. Console.WriteFile.argtypes = [HANDLE, LPCVOID, DWORD, LPDWORD, c_void_p] #HANDLE, LPCVOID , DWORD, LPDWORD , LPOVERLAPPED
  584. from .event import Event
  585. VkKeyScan = windll.user32.VkKeyScanA
  586. class event(Event):
  587. '''Represent events from the console.'''
  588. def __init__(self, console, input):
  589. '''Initialize an event from the Windows input structure.'''
  590. self.type = '??'
  591. self.serial = console.next_serial()
  592. self.width = 0
  593. self.height = 0
  594. self.x = 0
  595. self.y = 0
  596. self.char = ''
  597. self.keycode = 0
  598. self.keysym = '??'
  599. self.keyinfo = None # a tuple with (control, meta, shift, keycode) for dispatch
  600. self.width = None
  601. if input.EventType == KEY_EVENT:
  602. if input.Event.KeyEvent.bKeyDown:
  603. self.type = "KeyPress"
  604. else:
  605. self.type = "KeyRelease"
  606. self.char = input.Event.KeyEvent.uChar.UnicodeChar
  607. self.keycode = input.Event.KeyEvent.wVirtualKeyCode
  608. self.state = input.Event.KeyEvent.dwControlKeyState
  609. self.keyinfo = make_KeyPress(self.char,self.state,self.keycode)
  610. elif input.EventType == MOUSE_EVENT:
  611. if input.Event.MouseEvent.dwEventFlags & MOUSE_MOVED:
  612. self.type = "Motion"
  613. else:
  614. self.type = "Button"
  615. self.x = input.Event.MouseEvent.dwMousePosition.X
  616. self.y = input.Event.MouseEvent.dwMousePosition.Y
  617. self.state = input.Event.MouseEvent.dwButtonState
  618. elif input.EventType == WINDOW_BUFFER_SIZE_EVENT:
  619. self.type = "Configure"
  620. self.width = input.Event.WindowBufferSizeEvent.dwSize.X
  621. self.height = input.Event.WindowBufferSizeEvent.dwSize.Y
  622. elif input.EventType == FOCUS_EVENT:
  623. if input.Event.FocusEvent.bSetFocus:
  624. self.type = "FocusIn"
  625. else:
  626. self.type = "FocusOut"
  627. elif input.EventType == MENU_EVENT:
  628. self.type = "Menu"
  629. self.state = input.Event.MenuEvent.dwCommandId
  630. def getconsole(buffer=1):
  631. """Get a console handle.
  632. If buffer is non-zero, a new console buffer is allocated and
  633. installed. Otherwise, this returns a handle to the current
  634. console buffer"""
  635. c = Console(buffer)
  636. return c
  637. # The following code uses ctypes to allow a Python callable to
  638. # substitute for GNU readline within the Python interpreter. Calling
  639. # raw_input or other functions that do input, inside your callable
  640. # might be a bad idea, then again, it might work.
  641. # The Python callable can raise EOFError or KeyboardInterrupt and
  642. # these will be translated into the appropriate outputs from readline
  643. # so that they will then be translated back!
  644. # If the Python callable raises any other exception, a traceback will
  645. # be printed and readline will appear to return an empty line.
  646. # I use ctypes to create a C-callable from a Python wrapper that
  647. # handles the exceptions and gets the result into the right form.
  648. # the type for our C-callable wrapper
  649. HOOKFUNC23 = CFUNCTYPE(c_char_p, c_void_p, c_void_p, c_char_p)
  650. readline_hook = None # the python hook goes here
  651. readline_ref = None # reference to the c-callable to keep it alive
  652. def hook_wrapper_23(stdin, stdout, prompt):
  653. '''Wrap a Python readline so it behaves like GNU readline.'''
  654. try:
  655. # call the Python hook
  656. res = ensure_str(readline_hook(prompt))
  657. # make sure it returned the right sort of thing
  658. if res and not isinstance(res, bytes):
  659. raise TypeError('readline must return a string.')
  660. except KeyboardInterrupt:
  661. # GNU readline returns 0 on keyboard interrupt
  662. return 0
  663. except EOFError:
  664. # It returns an empty string on EOF
  665. res = ensure_str('')
  666. except:
  667. print('Readline internal error', file=sys.stderr)
  668. traceback.print_exc()
  669. res = ensure_str('\n')
  670. # we have to make a copy because the caller expects to free the result
  671. n = len(res)
  672. p = Console.PyMem_Malloc(n + 1)
  673. _strncpy(cast(p, c_char_p), res, n + 1)
  674. return p
  675. def install_readline(hook):
  676. '''Set up things for the interpreter to call
  677. our function like GNU readline.'''
  678. global readline_hook, readline_ref
  679. # save the hook so the wrapper can call it
  680. readline_hook = hook
  681. # get the address of PyOS_ReadlineFunctionPointer so we can update it
  682. PyOS_RFP = c_void_p.from_address(Console.GetProcAddress(sys.dllhandle,
  683. "PyOS_ReadlineFunctionPointer".encode('ascii')))
  684. # save a reference to the generated C-callable so it doesn't go away
  685. readline_ref = HOOKFUNC23(hook_wrapper_23)
  686. # get the address of the function
  687. func_start = c_void_p.from_address(addressof(readline_ref)).value
  688. # write the function address into PyOS_ReadlineFunctionPointer
  689. PyOS_RFP.value = func_start
  690. if __name__ == '__main__':
  691. import time, sys
  692. def p(char):
  693. return chr(VkKeyScan(ord(char)) & 0xff)
  694. c = Console(0)
  695. sys.stdout = c
  696. sys.stderr = c
  697. c.page()
  698. print(p("d"), p("D"))
  699. c.pos(5, 10)
  700. c.write('hi there')
  701. print('some printed output')
  702. for i in range(10):
  703. q = c.getkeypress()
  704. print(q)
  705. del c