99from rdfm .api import wrap_api_error
1010import urllib
1111import rdfm .ws
12+ import tty
13+ import termios
14+ import os
1215
1316
1417def shell_ws_url (server_url : str , device : str ) -> str :
@@ -99,6 +102,7 @@ def __init__(
99102 self .writer_thread = threading .Thread (
100103 target = self .__writer_thread , daemon = True
101104 )
105+ self .termattrs = None
102106
103107 def __reader_thread (self ):
104108 """WebSocket reader thread
@@ -117,25 +121,42 @@ def __reader_thread(self):
117121 ):
118122 self .closed .set ()
119123
124+ def __prepare_tty (self ):
125+ """Put the terminal into raw mode and preserve previous terminal
126+ attributes.
127+ """
128+ fd = sys .stdin .fileno ()
129+ if os .isatty (fd ):
130+ self .termattrs = termios .tcgetattr (fd )
131+ tty .setraw (fd )
132+
133+ def __restore_tty (self ):
134+ """Restore the previous state of the terminal after shell was
135+ finished.
136+ """
137+ fd = sys .stdin .fileno ()
138+ if os .isatty (fd ):
139+ termios .tcsetattr (fd , termios .TCSADRAIN , self .termattrs )
140+
120141 def __writer_thread (self ):
121142 """STDIN reader thread
122143
123144 This reads user input from STDIN and sends it to the shell WS
124145 """
146+ fd = sys .stdin .fileno ()
125147 try :
126- # Avoid blocking on readline (). Otherwise, when the connection is
148+ # Avoid blocking on read (). Otherwise, when the connection is
127149 # already closed, the writer thread will be blocked in a kernel
128150 # call to read(). By avoiding the read call until we know data is
129151 # there, we can avoid an annoying input prompt when rdfm-mgmt is
130152 # closing.
131- # FIXME: select does not allow for monitoring file fd's on Windows
132153 while True :
133- r , _ , _ = select .select ([sys . stdin . fileno () ], [], [], 1.0 )
154+ r , _ , _ = select .select ([fd ], [], [], 1.0 )
134155 if len (r ) > 0 :
135- data = sys . stdin . readline ( )
156+ data = os . read ( fd , 4096 )
136157 if len (data ) == 0 :
137158 break
138- self .ws .send (data . encode () )
159+ self .ws .send (data )
139160 if self .closed .is_set ():
140161 break
141162 finally :
@@ -148,13 +169,15 @@ def run(self):
148169 (either by the server/device disconnecting, or user interrupting with
149170 Ctrl-C/Ctrl-D).
150171 """
172+ self .__prepare_tty ()
151173 self .reader_thread .start ()
152174 self .writer_thread .start ()
153175
154176 try :
155177 self .closed .wait ()
156178 except (KeyboardInterrupt , EOFError ):
157179 pass
180+ self .__restore_tty ()
158181
159182 if self .ws .connected :
160183 # Normal exit (Ctrl-C / EOF on STDIN)
0 commit comments