From 19c13785701a5030eeafc268f6762f37c53eb76a Mon Sep 17 00:00:00 2001 From: ramin Date: Sat, 8 Sep 2018 15:32:26 +0200 Subject: [PATCH 1/6] colors in Windows Terminal --- rshell/main.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rshell/main.py b/rshell/main.py index 771ccdb..9897072 100755 --- a/rshell/main.py +++ b/rshell/main.py @@ -46,7 +46,9 @@ from serial.tools import list_ports import traceback - +if sys.platform == 'win32': + from colorama import init + init() # I got the following from: http://www.farmckon.net/2009/08/rlcompleter-how-do-i-get-it-to-work/ # Under OSX, if you call input with a prompt which contains ANSI escape @@ -64,7 +66,7 @@ import readline import rlcompleter -if 'libedit' in readline.__doc__: +if readline.__doc__ and 'libedit' in readline.__doc__: readline.parse_and_bind ("bind ^I rl_complete") BROKEN_READLINE = True else: From 4a0644775b5f1f9399b625a7cbe8f5e569ddb93d Mon Sep 17 00:00:00 2001 From: ramin Date: Mon, 10 Sep 2018 19:38:25 +0200 Subject: [PATCH 2/6] Win32: cd works with abs and relative paths --- rshell/main.py | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/rshell/main.py b/rshell/main.py index 9897072..9a2c990 100755 --- a/rshell/main.py +++ b/rshell/main.py @@ -434,28 +434,34 @@ def resolve_path(path): if path[0] == '~': # ~ or ~user path = os.path.expanduser(path) - if path[0] != '/': + if sys.platform == 'win32': + if not os.path.isabs(path): # not an absolute path + path = os.path.join(cur_dir, path) + path = os.path.normpath(path) + else: + if path[0] != '/': # Relative path - if cur_dir[-1] == '/': - path = cur_dir + path - else: - path = cur_dir + '/' + path - comps = path.split('/') - new_comps = [] - for comp in comps: - # We strip out xxx/./xxx and xxx//xxx, except that we want to keep the - # leading / for absolute paths. This also removes the trailing slash - # that autocompletion adds to a directory. - if comp == '.' or (comp == '' and len(new_comps) > 0): - continue - if comp == '..': - if len(new_comps) > 1: - new_comps.pop() - else: - new_comps.append(comp) - if len(new_comps) == 1 and new_comps[0] == '': - return '/' - return '/'.join(new_comps) + if cur_dir[-1] == '/': + path = cur_dir + path + else: + path = cur_dir + '/' + path + comps = path.split('/') + new_comps = [] + for comp in comps: + # We strip out xxx/./xxx and xxx//xxx, except that we want to keep the + # leading / for absolute paths. This also removes the trailing slash + # that autocompletion adds to a directory. + if comp == '.' or (comp == '' and len(new_comps) > 0): + continue + if comp == '..': + if len(new_comps) > 1: + new_comps.pop() + else: + new_comps.append(comp) + if len(new_comps) == 1 and new_comps[0] == '': + return '/' + path = '/'.join(new_comps) + return path def get_dev_and_path(filename): From d0657470e2cc3dfe922207b933401cd3b823926e Mon Sep 17 00:00:00 2001 From: ramin Date: Mon, 10 Sep 2018 20:05:21 +0200 Subject: [PATCH 3/6] windows path handling should not affect paths starting with /pyboard --- rshell/main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rshell/main.py b/rshell/main.py index 9a2c990..0e3c9a2 100755 --- a/rshell/main.py +++ b/rshell/main.py @@ -434,7 +434,8 @@ def resolve_path(path): if path[0] == '~': # ~ or ~user path = os.path.expanduser(path) - if sys.platform == 'win32': + # is there a nicer way to detect if we are local or remote ? + if sys.platform == 'win32' and not path.startswith('/pyboard'): if not os.path.isabs(path): # not an absolute path path = os.path.join(cur_dir, path) path = os.path.normpath(path) From 96adbb8a51f477fd20cea3cc78e3a425372eb4c0 Mon Sep 17 00:00:00 2001 From: ramin Date: Mon, 10 Sep 2018 20:17:23 +0200 Subject: [PATCH 4/6] win32: edit remote files --- rshell/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rshell/main.py b/rshell/main.py index 0e3c9a2..9f85ffa 100755 --- a/rshell/main.py +++ b/rshell/main.py @@ -2084,7 +2084,10 @@ def do_edit(self, line): print('Retrieving {} ...'.format(filename)) cp(filename, local_filename) old_stat = get_stat(local_filename) - os.system("{} '{}'".format(EDITOR, local_filename)) + if sys.platform == 'win32': + os.system('{} "{}"'.format(EDITOR, local_filename)) + else: + os.system("{} '{}'".format(EDITOR, local_filename)) new_stat = get_stat(local_filename) if old_stat != new_stat: self.print('Updating {} ...'.format(filename)) From a43505ec298b8991f5216443440c60f4b515e906 Mon Sep 17 00:00:00 2001 From: ramin Date: Mon, 10 Sep 2018 21:04:29 +0200 Subject: [PATCH 5/6] Win32: backspace in REPL works --- rshell/main.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/rshell/main.py b/rshell/main.py index 9f85ffa..a752956 100755 --- a/rshell/main.py +++ b/rshell/main.py @@ -2075,7 +2075,10 @@ def do_edit(self, line): return if dev is None: # File is local - os.system("{} '{}'".format(EDITOR, filename)) + if sys.platform == 'win32': + os.system('{} "{}"'.format(EDITOR, filename)) + else: + os.system("{} '{}'".format(EDITOR, filename)) else: # File is remote with tempfile.TemporaryDirectory() as temp_dir: @@ -2252,6 +2255,7 @@ def repl_serial_to_stdout(self, dev): the seril port and writing them to stdout. Used by do_repl. """ print('repl_serial_to_stdout dev =', dev) + last_char = None with self.serial_reader_running: try: save_timeout = dev.timeout @@ -2280,8 +2284,19 @@ def repl_serial_to_stdout(self, dev): if self.quit_when_no_output: break continue - self.stdout.write(char) + if sys.platform == 'win32': + # a backspace results in the following ascii values: + # 8, 27, 91, 75 + if ord(char) == 8: # handle backspace + self.stdout.write('\b \b') + elif last_char and (ord(last_char), ord(char)) in ((8, 27), (27, 91), (91, 75)): + pass + else: + self.stdout.write(char) + else: + self.stdout.write(char) self.stdout.flush() + last_char = char dev.timeout = save_timeout except DeviceError: # The device is no longer present. From 705fbdc490c1ab461c6ed49b9257ea4f7f3bedf1 Mon Sep 17 00:00:00 2001 From: ramin Date: Wed, 12 Sep 2018 19:32:45 +0200 Subject: [PATCH 6/6] windows: VT100 compatibility, edit command fixed --- rshell/main.py | 103 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 13 deletions(-) diff --git a/rshell/main.py b/rshell/main.py index a752956..250e99f 100755 --- a/rshell/main.py +++ b/rshell/main.py @@ -47,8 +47,61 @@ import traceback if sys.platform == 'win32': + import ctypes from colorama import init init() + + # def compat_ctypes_WINFUNCTYPE(*args, **kwargs): + # return ctypes.WINFUNCTYPE(*args, **kwargs) + def _windows_enable_ANSI(std_id): + """Enable Windows 10 cmd.exe ANSI VT Virtual Terminal Processing. + adapted from http://stackoverflow.com/a/3259271/35070 + """ + from ctypes import byref, POINTER, windll, WINFUNCTYPE + from ctypes.wintypes import BOOL, DWORD, HANDLE + + GetStdHandle = ctypes.WINFUNCTYPE( + HANDLE, + DWORD)(('GetStdHandle', windll.kernel32)) + + GetFileType = ctypes.WINFUNCTYPE( + DWORD, + HANDLE)(('GetFileType', windll.kernel32)) + + GetConsoleMode = ctypes.WINFUNCTYPE( + BOOL, + HANDLE, + POINTER(DWORD))(('GetConsoleMode', windll.kernel32)) + + SetConsoleMode = ctypes.WINFUNCTYPE( + BOOL, + HANDLE, + DWORD)(('SetConsoleMode', windll.kernel32)) + + if std_id == 1: # stdout + h = GetStdHandle(-11) + elif std_id == 2: # stderr + h = GetStdHandle(-12) + else: + return False + + if h is None or h == HANDLE(-1): + return False + + FILE_TYPE_CHAR = 0x0002 + if (GetFileType(h) & 3) != FILE_TYPE_CHAR: + return False + + mode = DWORD() + if not GetConsoleMode(h, byref(mode)): + return False + + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0: + SetConsoleMode(h, mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING) + return True + _windows_enable_ANSI(1) + # I got the following from: http://www.farmckon.net/2009/08/rlcompleter-how-do-i-get-it-to-work/ # Under OSX, if you call input with a prompt which contains ANSI escape @@ -2255,7 +2308,6 @@ def repl_serial_to_stdout(self, dev): the seril port and writing them to stdout. Used by do_repl. """ print('repl_serial_to_stdout dev =', dev) - last_char = None with self.serial_reader_running: try: save_timeout = dev.timeout @@ -2284,19 +2336,8 @@ def repl_serial_to_stdout(self, dev): if self.quit_when_no_output: break continue - if sys.platform == 'win32': - # a backspace results in the following ascii values: - # 8, 27, 91, 75 - if ord(char) == 8: # handle backspace - self.stdout.write('\b \b') - elif last_char and (ord(last_char), ord(char)) in ((8, 27), (27, 91), (91, 75)): - pass - else: - self.stdout.write(char) - else: - self.stdout.write(char) + self.stdout.write(char) self.stdout.flush() - last_char = char dev.timeout = save_timeout except DeviceError: # The device is no longer present. @@ -2347,10 +2388,45 @@ def do_repl(self, line): dev.write(bytes(line, encoding='utf-8')) dev.write(b'\r') if not self.quit_when_no_output: + last_char = None while self.serial_reader_running(): char = getch() if not char: continue + if sys.platform == 'win32': + if ord(char) in (8, 0xe0): + last_char = char + continue + if last_char: + if (ord(last_char), ord(char)) == (8, 0): # backspace + last_char = None + dev.write(b'\x7f') + continue + if (ord(last_char), ord(char)) == (0xe0, 0x4b): # cursor left + last_char = None + dev.write(b'\x1b[D') + continue + elif (ord(last_char), ord(char)) == (0xe0, 0x4d): # cursor right + last_char = None + dev.write(b'\x1b[C') + continue + elif (ord(last_char), ord(char)) == (0xe0, 0x48): # cursor up + last_char = None + dev.write(b'\x1b[A') + continue + elif (ord(last_char), ord(char)) == (0xe0, 0x50): # cursor down + last_char = None + dev.write(b'\x1b[B') + continue + elif (ord(last_char), ord(char)) == (0xe0, 0x47): # POS 1 + last_char = None + dev.write(b'\x1b[H') + continue + elif (ord(last_char), ord(char)) == (0xe0, 0x4f): # END + last_char = None + dev.write(b'\x1b[F') + continue + if char == QUIT_REPL_BYTE: self.quit_serial_reader = True # When using telnet with the WiPy, it doesn't support @@ -2373,6 +2449,7 @@ def do_repl(self, line): dev.write(b'\r') else: dev.write(char) + last_char = char except DeviceError as err: # The device is no longer present. self.print('')