Skip to content

Commit 8c368d0

Browse files
committed
Revert "Refactor termios stuff in unix console"
This reverts commit 2a7e81d.
1 parent aa9eaf3 commit 8c368d0

File tree

3 files changed

+219
-375
lines changed

3 files changed

+219
-375
lines changed

Lib/_pyrepl/_minimal_curses.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""Minimal '_curses' module, the low-level interface for curses module
2+
which is not meant to be used directly.
3+
4+
Based on ctypes. It's too incomplete to be really called '_curses', so
5+
to use it, you have to import it and stick it in sys.modules['_curses']
6+
manually.
7+
8+
Note that there is also a built-in module _minimal_curses which will
9+
hide this one if compiled in.
10+
"""
11+
12+
import ctypes
13+
import ctypes.util
14+
15+
16+
class error(Exception):
17+
pass
18+
19+
20+
def _find_clib():
21+
trylibs = ["ncursesw", "ncurses", "curses"]
22+
23+
for lib in trylibs:
24+
path = ctypes.util.find_library(lib)
25+
if path:
26+
return path
27+
raise ModuleNotFoundError("curses library not found", name="_minimal_curses")
28+
29+
30+
_clibpath = _find_clib()
31+
clib = ctypes.cdll.LoadLibrary(_clibpath)
32+
33+
clib.setupterm.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.POINTER(ctypes.c_int)]
34+
clib.setupterm.restype = ctypes.c_int
35+
36+
clib.tigetstr.argtypes = [ctypes.c_char_p]
37+
clib.tigetstr.restype = ctypes.POINTER(ctypes.c_char)
38+
39+
clib.tparm.argtypes = [ctypes.c_char_p] + 9 * [ctypes.c_int]
40+
clib.tparm.restype = ctypes.c_char_p
41+
42+
OK = 0
43+
ERR = -1
44+
45+
# ____________________________________________________________
46+
47+
48+
def setupterm(termstr, fd):
49+
err = ctypes.c_int(0)
50+
result = clib.setupterm(termstr, fd, ctypes.byref(err))
51+
if result == ERR:
52+
raise error("setupterm() failed (err=%d)" % err.value)
53+
54+
55+
def tigetstr(cap):
56+
if not isinstance(cap, bytes):
57+
cap = cap.encode("ascii")
58+
result = clib.tigetstr(cap)
59+
if ctypes.cast(result, ctypes.c_void_p).value == ERR:
60+
return None
61+
return ctypes.cast(result, ctypes.c_char_p).value
62+
63+
64+
def tparm(str, i1=0, i2=0, i3=0, i4=0, i5=0, i6=0, i7=0, i8=0, i9=0):
65+
result = clib.tparm(str, i1, i2, i3, i4, i5, i6, i7, i8, i9)
66+
if result is None:
67+
raise error("tparm() returned NULL")
68+
return result

Lib/_pyrepl/fancy_termios.py

Lines changed: 13 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,9 @@
1717
# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1818
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1919

20-
import curses
2120
import termios
2221

2322

24-
class InvalidTerminal(RuntimeError):
25-
pass
26-
27-
2823
class TermState:
2924
def __init__(self, tuples):
3025
(
@@ -60,212 +55,20 @@ def tcsetattr(fd, when, attrs):
6055
termios.tcsetattr(fd, when, attrs.as_list())
6156

6257

63-
class TermCapability:
64-
"""Base class for all terminal capabilities we need"""
65-
66-
def __init__(self, buffer):
67-
self._buffer = buffer
68-
self._args = []
69-
70-
self._bytes = curses.tigetstr(self.name)
71-
if self._bytes is None and not self.optional:
72-
raise InvalidTerminal(
73-
f"terminal doesn't have the required {self.name!r} capability"
74-
)
75-
76-
self.supported = self._bytes is not None
77-
78-
def __call__(self, *args):
79-
if self.supported:
80-
self._args = args
81-
self._buffer.push(self)
82-
83-
def text(self):
84-
return curses.tparm(self._bytes, *self._args)
85-
86-
87-
class Bell(TermCapability):
88-
"""Audible signal (bell)"""
89-
90-
name = "bel"
91-
optional = False
92-
93-
94-
class CursorInvisible(TermCapability):
95-
"""Make cursor invisible"""
96-
97-
name = "civis"
98-
optional = True
99-
100-
101-
class ClearScreen(TermCapability):
102-
"""Clear screen and home cursor"""
103-
104-
name = "clear"
105-
optional = False
106-
107-
108-
class CursorNormal(TermCapability):
109-
"""Make cursor appear normal (undo civis/cvvis)"""
110-
111-
name = "cnorm"
112-
optional = True
113-
114-
115-
class ParmLeftCursor(TermCapability):
116-
"""Move #1 characters to the left"""
117-
118-
name = "cub"
119-
optional = True
120-
121-
122-
class CursorLeft(TermCapability):
123-
"""Move left one space"""
124-
125-
name = "cub1"
126-
optional = True
127-
128-
def text(self):
129-
assert len(self._args) == 1 # cursor_down needs to have been called with dx
130-
return curses.tparm(self._args[0] * self._bytes)
131-
132-
133-
class ParmDownCursor(TermCapability):
134-
"""Down #1 lines"""
135-
136-
name = "cud"
137-
optional = True
138-
139-
140-
class CursorDown(TermCapability):
141-
"""Down one line"""
142-
143-
name = "cud1"
144-
optional = True
145-
146-
def text(self):
147-
assert len(self._args) == 1 # cursor_down needs to have been called with dy
148-
return curses.tparm(self._args[0] * self._bytes)
149-
150-
151-
class ParmRightCursor(TermCapability):
152-
"""Move #1 characters to the right"""
153-
154-
name = "cuf"
155-
optional = True
156-
157-
158-
class CursorRight(TermCapability):
159-
"""Non-destructive space (move right one space)"""
160-
161-
name = "cuf1"
162-
optional = True
163-
164-
def text(self):
165-
assert len(self._args) == 1 # cursor_down needs to have been called with dx
166-
return curses.tparm(self._args[0] * self._bytes)
167-
168-
169-
class CursorAddress(TermCapability):
170-
"""Move to row #1 columns #2"""
171-
172-
name = "cup"
173-
optional = False
174-
175-
176-
class ParmUpCursor(TermCapability):
177-
"""Up #1 lines"""
178-
179-
name = "cuu"
180-
optional = True
181-
182-
183-
class CursorUp(TermCapability):
184-
"""Up 1 line"""
185-
186-
name = "cuu1"
187-
optional = True
188-
189-
def text(self):
190-
assert len(self._args) == 1 # cursor_down needs to have been called with dy
191-
return curses.tparm(self._args[0] * self._bytes)
192-
193-
194-
class ParmDeleteCharacter(TermCapability):
195-
"""Delete #1 characters"""
196-
197-
name = "dch"
198-
optional = True
199-
200-
201-
class DeleteCharacter(TermCapability):
202-
"""Delete character"""
203-
204-
name = "dch1"
205-
optional = True
206-
207-
208-
class ClearEol(TermCapability):
209-
"""Clear to end of line"""
210-
211-
name = "el"
212-
optional = False
213-
214-
215-
class ColumnAddress(TermCapability):
216-
"""Horizontal position #1, absolute"""
217-
218-
name = "hpa"
219-
optional = True
220-
221-
222-
class ParmInsertCharacter(TermCapability):
223-
"""Insert #1 characters"""
224-
225-
name = "ich"
226-
optional = True
227-
228-
229-
class InsertCharacter(TermCapability):
230-
"""Insert character"""
231-
232-
name = "ich1"
233-
optional = True
234-
235-
236-
class ScrollForward(TermCapability):
237-
"""Scroll text up"""
238-
239-
name = "ind"
240-
optional = True
241-
242-
243-
class PadChar(TermCapability):
244-
"""Padding char (instead of null)"""
245-
246-
name = "pad"
247-
optional = True
248-
249-
def text(self, nchars):
250-
return self._bytes * nchars
251-
252-
253-
class ScrollReverse(TermCapability):
254-
"""Scroll text down"""
255-
256-
name = "ri"
257-
optional = True
258-
259-
260-
class KeypadLocal(TermCapability):
261-
"""Leave 'keyboard_transmit' mode"""
58+
class Term(TermState):
59+
TS__init__ = TermState.__init__
26260

263-
name = "rmkx"
264-
optional = True
61+
def __init__(self, fd=0):
62+
self.TS__init__(termios.tcgetattr(fd))
63+
self.fd = fd
64+
self.stack = []
26565

66+
def save(self):
67+
self.stack.append(self.as_list())
26668

267-
class KeypadXmit(TermCapability):
268-
"""Enter 'keyboard_transmit' mode"""
69+
def set(self, when=termios.TCSANOW):
70+
termios.tcsetattr(self.fd, when, self.as_list())
26971

270-
name = "smkx"
271-
optional = True
72+
def restore(self):
73+
self.TS__init__(self.stack.pop())
74+
self.set()

0 commit comments

Comments
 (0)