urwid 2.6.0.post0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of urwid might be problematic. Click here for more details.
- urwid/__init__.py +333 -0
- urwid/canvas.py +1413 -0
- urwid/command_map.py +137 -0
- urwid/container.py +59 -0
- urwid/decoration.py +65 -0
- urwid/display/__init__.py +97 -0
- urwid/display/_posix_raw_display.py +413 -0
- urwid/display/_raw_display_base.py +914 -0
- urwid/display/_web.css +12 -0
- urwid/display/_web.js +462 -0
- urwid/display/_win32.py +171 -0
- urwid/display/_win32_raw_display.py +269 -0
- urwid/display/common.py +1219 -0
- urwid/display/curses.py +690 -0
- urwid/display/escape.py +624 -0
- urwid/display/html_fragment.py +251 -0
- urwid/display/lcd.py +518 -0
- urwid/display/raw.py +37 -0
- urwid/display/web.py +636 -0
- urwid/event_loop/__init__.py +55 -0
- urwid/event_loop/abstract_loop.py +175 -0
- urwid/event_loop/asyncio_loop.py +231 -0
- urwid/event_loop/glib_loop.py +294 -0
- urwid/event_loop/main_loop.py +721 -0
- urwid/event_loop/select_loop.py +230 -0
- urwid/event_loop/tornado_loop.py +206 -0
- urwid/event_loop/trio_loop.py +302 -0
- urwid/event_loop/twisted_loop.py +269 -0
- urwid/event_loop/zmq_loop.py +275 -0
- urwid/font.py +695 -0
- urwid/graphics.py +96 -0
- urwid/highlight.css +19 -0
- urwid/listbox.py +1899 -0
- urwid/monitored_list.py +522 -0
- urwid/numedit.py +376 -0
- urwid/signals.py +330 -0
- urwid/split_repr.py +130 -0
- urwid/str_util.py +358 -0
- urwid/text_layout.py +632 -0
- urwid/treetools.py +515 -0
- urwid/util.py +557 -0
- urwid/version.py +16 -0
- urwid/vterm.py +1806 -0
- urwid/widget/__init__.py +181 -0
- urwid/widget/attr_map.py +161 -0
- urwid/widget/attr_wrap.py +140 -0
- urwid/widget/bar_graph.py +649 -0
- urwid/widget/big_text.py +77 -0
- urwid/widget/box_adapter.py +126 -0
- urwid/widget/columns.py +1145 -0
- urwid/widget/constants.py +574 -0
- urwid/widget/container.py +227 -0
- urwid/widget/divider.py +110 -0
- urwid/widget/edit.py +718 -0
- urwid/widget/filler.py +403 -0
- urwid/widget/frame.py +539 -0
- urwid/widget/grid_flow.py +539 -0
- urwid/widget/line_box.py +194 -0
- urwid/widget/overlay.py +829 -0
- urwid/widget/padding.py +597 -0
- urwid/widget/pile.py +971 -0
- urwid/widget/popup.py +170 -0
- urwid/widget/progress_bar.py +141 -0
- urwid/widget/scrollable.py +597 -0
- urwid/widget/solid_fill.py +44 -0
- urwid/widget/text.py +354 -0
- urwid/widget/widget.py +852 -0
- urwid/widget/widget_decoration.py +166 -0
- urwid/widget/wimp.py +792 -0
- urwid/wimp.py +23 -0
- urwid-2.6.0.post0.dist-info/COPYING +504 -0
- urwid-2.6.0.post0.dist-info/METADATA +332 -0
- urwid-2.6.0.post0.dist-info/RECORD +75 -0
- urwid-2.6.0.post0.dist-info/WHEEL +5 -0
- urwid-2.6.0.post0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# Urwid raw display module
|
|
2
|
+
# Copyright (C) 2004-2009 Ian Ward
|
|
3
|
+
#
|
|
4
|
+
# This library is free software; you can redistribute it and/or
|
|
5
|
+
# modify it under the terms of the GNU Lesser General Public
|
|
6
|
+
# License as published by the Free Software Foundation; either
|
|
7
|
+
# version 2.1 of the License, or (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
12
|
+
# Lesser General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
|
15
|
+
# License along with this library; if not, write to the Free Software
|
|
16
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
17
|
+
#
|
|
18
|
+
# Urwid web site: https://urwid.org/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
Direct terminal UI implementation
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import contextlib
|
|
28
|
+
import functools
|
|
29
|
+
import logging
|
|
30
|
+
import selectors
|
|
31
|
+
import socket
|
|
32
|
+
import sys
|
|
33
|
+
import threading
|
|
34
|
+
import typing
|
|
35
|
+
from ctypes import byref
|
|
36
|
+
from ctypes.wintypes import DWORD
|
|
37
|
+
|
|
38
|
+
from urwid import signals
|
|
39
|
+
|
|
40
|
+
from . import _raw_display_base, _win32, escape
|
|
41
|
+
from .common import INPUT_DESCRIPTORS_CHANGED
|
|
42
|
+
|
|
43
|
+
if typing.TYPE_CHECKING:
|
|
44
|
+
import io
|
|
45
|
+
from collections.abc import Callable
|
|
46
|
+
|
|
47
|
+
from urwid.event_loop import EventLoop
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Screen(_raw_display_base.Screen):
|
|
51
|
+
_term_input_file: socket.socket
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
input: socket.socket | None = None, # noqa: A002 # pylint: disable=redefined-builtin
|
|
56
|
+
output: io.TextIOBase = sys.stdout,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""Initialize a screen that directly prints escape codes to an output
|
|
59
|
+
terminal.
|
|
60
|
+
"""
|
|
61
|
+
if input is None:
|
|
62
|
+
input, self._send_input = socket.socketpair() # noqa: A001
|
|
63
|
+
|
|
64
|
+
super().__init__(input, output)
|
|
65
|
+
|
|
66
|
+
_dwOriginalOutMode = None
|
|
67
|
+
_dwOriginalInMode = None
|
|
68
|
+
|
|
69
|
+
def _start(self, alternate_buffer: bool = True) -> None:
|
|
70
|
+
"""
|
|
71
|
+
Initialize the screen and input mode.
|
|
72
|
+
|
|
73
|
+
alternate_buffer -- use alternate screen buffer
|
|
74
|
+
"""
|
|
75
|
+
if alternate_buffer:
|
|
76
|
+
self.write(escape.SWITCH_TO_ALTERNATE_BUFFER)
|
|
77
|
+
self._rows_used = None
|
|
78
|
+
else:
|
|
79
|
+
self._rows_used = 0
|
|
80
|
+
|
|
81
|
+
handle_out = _win32.GetStdHandle(_win32.STD_OUTPUT_HANDLE)
|
|
82
|
+
handle_in = _win32.GetStdHandle(_win32.STD_INPUT_HANDLE)
|
|
83
|
+
self._dwOriginalOutMode = DWORD()
|
|
84
|
+
self._dwOriginalInMode = DWORD()
|
|
85
|
+
_win32.GetConsoleMode(handle_out, byref(self._dwOriginalOutMode))
|
|
86
|
+
_win32.GetConsoleMode(handle_in, byref(self._dwOriginalInMode))
|
|
87
|
+
# TODO: Restore on exit
|
|
88
|
+
|
|
89
|
+
dword_out_mode = DWORD(
|
|
90
|
+
self._dwOriginalOutMode.value
|
|
91
|
+
| _win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
92
|
+
| _win32.DISABLE_NEWLINE_AUTO_RETURN
|
|
93
|
+
)
|
|
94
|
+
dword_in_mode = DWORD(
|
|
95
|
+
self._dwOriginalInMode.value | _win32.ENABLE_WINDOW_INPUT | _win32.ENABLE_VIRTUAL_TERMINAL_INPUT
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
ok = _win32.SetConsoleMode(handle_out, dword_out_mode)
|
|
99
|
+
if not ok:
|
|
100
|
+
raise RuntimeError(f"ConsoleMode set failed for output. Err: {ok!r}")
|
|
101
|
+
ok = _win32.SetConsoleMode(handle_in, dword_in_mode)
|
|
102
|
+
if not ok:
|
|
103
|
+
raise RuntimeError(f"ConsoleMode set failed for input. Err: {ok!r}")
|
|
104
|
+
self._alternate_buffer = alternate_buffer
|
|
105
|
+
self._next_timeout = self.max_wait
|
|
106
|
+
|
|
107
|
+
signals.emit_signal(self, INPUT_DESCRIPTORS_CHANGED)
|
|
108
|
+
# restore mouse tracking to previous state
|
|
109
|
+
self._mouse_tracking(self._mouse_tracking_enabled)
|
|
110
|
+
|
|
111
|
+
return super()._start()
|
|
112
|
+
|
|
113
|
+
def _stop(self) -> None:
|
|
114
|
+
"""
|
|
115
|
+
Restore the screen.
|
|
116
|
+
"""
|
|
117
|
+
self.clear()
|
|
118
|
+
|
|
119
|
+
signals.emit_signal(self, INPUT_DESCRIPTORS_CHANGED)
|
|
120
|
+
|
|
121
|
+
self._stop_mouse_restore_buffer()
|
|
122
|
+
|
|
123
|
+
handle_out = _win32.GetStdHandle(_win32.STD_OUTPUT_HANDLE)
|
|
124
|
+
handle_in = _win32.GetStdHandle(_win32.STD_INPUT_HANDLE)
|
|
125
|
+
ok = _win32.SetConsoleMode(handle_out, self._dwOriginalOutMode)
|
|
126
|
+
if not ok:
|
|
127
|
+
raise RuntimeError(f"ConsoleMode set failed for output. Err: {ok!r}")
|
|
128
|
+
ok = _win32.SetConsoleMode(handle_in, self._dwOriginalInMode)
|
|
129
|
+
if not ok:
|
|
130
|
+
raise RuntimeError(f"ConsoleMode set failed for input. Err: {ok!r}")
|
|
131
|
+
|
|
132
|
+
super()._stop()
|
|
133
|
+
|
|
134
|
+
def unhook_event_loop(self, event_loop: EventLoop) -> None:
|
|
135
|
+
"""
|
|
136
|
+
Remove any hooks added by hook_event_loop.
|
|
137
|
+
"""
|
|
138
|
+
if self._input_thread is not None:
|
|
139
|
+
self._input_thread.should_exit = True
|
|
140
|
+
|
|
141
|
+
with contextlib.suppress(RuntimeError):
|
|
142
|
+
self._input_thread.join(5)
|
|
143
|
+
|
|
144
|
+
self._input_thread = None
|
|
145
|
+
|
|
146
|
+
for handle in self._current_event_loop_handles:
|
|
147
|
+
event_loop.remove_watch_file(handle)
|
|
148
|
+
|
|
149
|
+
if self._input_timeout:
|
|
150
|
+
event_loop.remove_alarm(self._input_timeout)
|
|
151
|
+
self._input_timeout = None
|
|
152
|
+
|
|
153
|
+
def hook_event_loop(
|
|
154
|
+
self,
|
|
155
|
+
event_loop: EventLoop,
|
|
156
|
+
callback: Callable[[list[str], list[int]], typing.Any],
|
|
157
|
+
) -> None:
|
|
158
|
+
"""
|
|
159
|
+
Register the given callback with the event loop, to be called with new
|
|
160
|
+
input whenever it's available. The callback should be passed a list of
|
|
161
|
+
processed keys and a list of unprocessed keycodes.
|
|
162
|
+
|
|
163
|
+
Subclasses may wish to use parse_input to wrap the callback.
|
|
164
|
+
"""
|
|
165
|
+
self._input_thread = ReadInputThread(self._send_input, lambda: self._sigwinch_handler(28))
|
|
166
|
+
self._input_thread.start()
|
|
167
|
+
if hasattr(self, "get_input_nonblocking"):
|
|
168
|
+
wrapper = self._make_legacy_input_wrapper(event_loop, callback)
|
|
169
|
+
else:
|
|
170
|
+
|
|
171
|
+
@functools.wraps(callback)
|
|
172
|
+
def wrapper() -> tuple[list[str], typing.Any] | None:
|
|
173
|
+
return self.parse_input(event_loop, callback, self.get_available_raw_input())
|
|
174
|
+
|
|
175
|
+
fds = self.get_input_descriptors()
|
|
176
|
+
handles = [event_loop.watch_file(fd if isinstance(fd, int) else fd.fileno(), wrapper) for fd in fds]
|
|
177
|
+
self._current_event_loop_handles = handles
|
|
178
|
+
|
|
179
|
+
_input_thread: ReadInputThread | None = None
|
|
180
|
+
|
|
181
|
+
def _read_raw_input(self, timeout: int) -> bytearray:
|
|
182
|
+
ready = self._wait_for_input_ready(timeout)
|
|
183
|
+
|
|
184
|
+
fd = self._input_fileno()
|
|
185
|
+
chars = bytearray()
|
|
186
|
+
|
|
187
|
+
if fd is None or fd not in ready:
|
|
188
|
+
return chars
|
|
189
|
+
|
|
190
|
+
with selectors.DefaultSelector() as selector:
|
|
191
|
+
selector.register(fd, selectors.EVENT_READ)
|
|
192
|
+
input_ready = selector.select(0)
|
|
193
|
+
while input_ready:
|
|
194
|
+
chars.extend(self._term_input_file.recv(1024))
|
|
195
|
+
input_ready = selector.select(0)
|
|
196
|
+
|
|
197
|
+
return chars
|
|
198
|
+
|
|
199
|
+
def get_cols_rows(self) -> tuple[int, int]:
|
|
200
|
+
"""Return the terminal dimensions (num columns, num rows)."""
|
|
201
|
+
y, x = super().get_cols_rows()
|
|
202
|
+
with contextlib.suppress(OSError): # Term size could not be determined
|
|
203
|
+
if hasattr(self._term_output_file, "fileno"):
|
|
204
|
+
if self._term_output_file != sys.stdout:
|
|
205
|
+
raise RuntimeError("Unexpected terminal output file")
|
|
206
|
+
handle = _win32.GetStdHandle(_win32.STD_OUTPUT_HANDLE)
|
|
207
|
+
info = _win32.CONSOLE_SCREEN_BUFFER_INFO()
|
|
208
|
+
ok = _win32.GetConsoleScreenBufferInfo(handle, byref(info))
|
|
209
|
+
if ok:
|
|
210
|
+
# Fallback will be used in case of term size could not be determined
|
|
211
|
+
y, x = info.dwSize.Y, info.dwSize.X
|
|
212
|
+
|
|
213
|
+
self.maxrow = y
|
|
214
|
+
return x, y
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class ReadInputThread(threading.Thread):
|
|
218
|
+
name = "urwid Windows input reader"
|
|
219
|
+
daemon = True
|
|
220
|
+
should_exit: bool = False
|
|
221
|
+
|
|
222
|
+
def __init__(
|
|
223
|
+
self,
|
|
224
|
+
input_socket: socket.socket,
|
|
225
|
+
resize: Callable[[], typing.Any],
|
|
226
|
+
) -> None:
|
|
227
|
+
self._input = input_socket
|
|
228
|
+
self._resize = resize
|
|
229
|
+
self.logger = logging.getLogger(__name__).getChild(self.__class__.__name__)
|
|
230
|
+
super().__init__()
|
|
231
|
+
|
|
232
|
+
def run(self) -> None:
|
|
233
|
+
hIn = _win32.GetStdHandle(_win32.STD_INPUT_HANDLE)
|
|
234
|
+
MAX = 2048
|
|
235
|
+
|
|
236
|
+
read = DWORD(0)
|
|
237
|
+
arrtype = _win32.INPUT_RECORD * MAX
|
|
238
|
+
input_records = arrtype()
|
|
239
|
+
|
|
240
|
+
while True:
|
|
241
|
+
_win32.ReadConsoleInputW(hIn, byref(input_records), MAX, byref(read))
|
|
242
|
+
if self.should_exit:
|
|
243
|
+
return
|
|
244
|
+
for i in range(read.value):
|
|
245
|
+
inp = input_records[i]
|
|
246
|
+
if inp.EventType == _win32.EventType.KEY_EVENT:
|
|
247
|
+
if not inp.Event.KeyEvent.bKeyDown:
|
|
248
|
+
continue
|
|
249
|
+
|
|
250
|
+
input_data = inp.Event.KeyEvent.uChar.AsciiChar
|
|
251
|
+
# On Windows atomic press/release of modifier keys produce phantom input with code NULL
|
|
252
|
+
# This input cannot be decoded and should be handled as garbage.
|
|
253
|
+
if input_data != b"\x00":
|
|
254
|
+
self._input.send(input_data)
|
|
255
|
+
|
|
256
|
+
elif inp.EventType == _win32.EventType.WINDOW_BUFFER_SIZE_EVENT:
|
|
257
|
+
self._resize()
|
|
258
|
+
else:
|
|
259
|
+
pass # TODO: handle mouse events
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _test():
|
|
263
|
+
import doctest
|
|
264
|
+
|
|
265
|
+
doctest.testmod()
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
if __name__ == "__main__":
|
|
269
|
+
_test()
|