multiSSH3 5.27__tar.gz → 5.33__tar.gz
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 multiSSH3 might be problematic. Click here for more details.
- {multissh3-5.27 → multissh3-5.33}/PKG-INFO +1 -1
- {multissh3-5.27 → multissh3-5.33}/multiSSH3.egg-info/PKG-INFO +1 -1
- {multissh3-5.27 → multissh3-5.33}/multiSSH3.py +429 -64
- {multissh3-5.27 → multissh3-5.33}/setup.py +1 -1
- {multissh3-5.27 → multissh3-5.33}/LICENSE +0 -0
- {multissh3-5.27 → multissh3-5.33}/README.md +0 -0
- {multissh3-5.27 → multissh3-5.33}/multiSSH3.egg-info/SOURCES.txt +0 -0
- {multissh3-5.27 → multissh3-5.33}/multiSSH3.egg-info/dependency_links.txt +0 -0
- {multissh3-5.27 → multissh3-5.33}/multiSSH3.egg-info/entry_points.txt +0 -0
- {multissh3-5.27 → multissh3-5.33}/multiSSH3.egg-info/requires.txt +0 -0
- {multissh3-5.27 → multissh3-5.33}/multiSSH3.egg-info/top_level.txt +0 -0
- {multissh3-5.27 → multissh3-5.33}/setup.cfg +0 -0
|
@@ -24,6 +24,7 @@ import shutil
|
|
|
24
24
|
import getpass
|
|
25
25
|
import uuid
|
|
26
26
|
import tempfile
|
|
27
|
+
import math
|
|
27
28
|
|
|
28
29
|
try:
|
|
29
30
|
# Check if functiools.cache is available
|
|
@@ -36,7 +37,7 @@ except AttributeError:
|
|
|
36
37
|
# If neither is available, use a dummy decorator
|
|
37
38
|
def cache_decorator(func):
|
|
38
39
|
return func
|
|
39
|
-
version = '5.
|
|
40
|
+
version = '5.33'
|
|
40
41
|
VERSION = version
|
|
41
42
|
|
|
42
43
|
CONFIG_FILE = '/etc/multiSSH3.config.json'
|
|
@@ -165,6 +166,7 @@ class Host:
|
|
|
165
166
|
self.stdout = [] # the stdout of the command
|
|
166
167
|
self.stderr = [] # the stderr of the command
|
|
167
168
|
self.printedLines = -1 # the number of lines printed on the screen
|
|
169
|
+
self.lineNumToReprintSet = set() # whether to reprint the last line
|
|
168
170
|
self.lastUpdateTime = time.time() # the last time the output was updated
|
|
169
171
|
self.files = files # the files to be copied to the host
|
|
170
172
|
self.ipmi = ipmi # whether to use ipmi to connect to the host
|
|
@@ -179,12 +181,17 @@ class Host:
|
|
|
179
181
|
self.uuid = uuid
|
|
180
182
|
self.identity_file = identity_file
|
|
181
183
|
self.ip = ip if ip else getIP(name)
|
|
184
|
+
self.current_color_pair = [-1, -1, 1]
|
|
182
185
|
|
|
183
186
|
def __iter__(self):
|
|
184
187
|
return zip(['name', 'command', 'returncode', 'stdout', 'stderr'], [self.name, self.command, self.returncode, self.stdout, self.stderr])
|
|
185
188
|
def __repr__(self):
|
|
186
189
|
# return the complete data structure
|
|
187
|
-
return f"Host(name={self.name}, command={self.command}, returncode={self.returncode}, stdout={self.stdout}, stderr={self.stderr},
|
|
190
|
+
return f"Host(name={self.name}, command={self.command}, returncode={self.returncode}, stdout={self.stdout}, stderr={self.stderr}, \
|
|
191
|
+
output={self.output}, printedLines={self.printedLines}, lineNumToReprintSet={self.lineNumToReprintSet}, files={self.files}, ipmi={self.ipmi}, \
|
|
192
|
+
interface_ip_prefix={self.interface_ip_prefix}, scp={self.scp}, gatherMode={self.gatherMode}, \
|
|
193
|
+
extraargs={self.extraargs}, resolvedName={self.resolvedName}, i={self.i}, uuid={self.uuid}), \
|
|
194
|
+
identity_file={self.identity_file}, ip={self.ip}, current_color_pair={self.current_color_pair}"
|
|
188
195
|
def __str__(self):
|
|
189
196
|
return f"Host(name={self.name}, command={self.command}, returncode={self.returncode}, stdout={self.stdout}, stderr={self.stderr})"
|
|
190
197
|
|
|
@@ -341,7 +348,30 @@ if True:
|
|
|
341
348
|
__keyPressesIn = [[]]
|
|
342
349
|
_emo = False
|
|
343
350
|
_etc_hosts = __configs_from_file.get('_etc_hosts', __build_in_default_config['_etc_hosts'])
|
|
344
|
-
|
|
351
|
+
__curses_global_color_pairs = {(-1,-1):1}
|
|
352
|
+
__curses_current_color_pair_index = 2 # Start from 1, as 0 is the default color pair
|
|
353
|
+
__curses_color_table = {}
|
|
354
|
+
__curses_current_color_index = 10
|
|
355
|
+
|
|
356
|
+
# Mapping of ANSI 4-bit colors to curses colors
|
|
357
|
+
ANSI_TO_CURSES_COLOR = {
|
|
358
|
+
30: curses.COLOR_BLACK,
|
|
359
|
+
31: curses.COLOR_RED,
|
|
360
|
+
32: curses.COLOR_GREEN,
|
|
361
|
+
33: curses.COLOR_YELLOW,
|
|
362
|
+
34: curses.COLOR_BLUE,
|
|
363
|
+
35: curses.COLOR_MAGENTA,
|
|
364
|
+
36: curses.COLOR_CYAN,
|
|
365
|
+
37: curses.COLOR_WHITE,
|
|
366
|
+
90: curses.COLOR_BLACK, # Bright Black (usually gray)
|
|
367
|
+
91: curses.COLOR_RED, # Bright Red
|
|
368
|
+
92: curses.COLOR_GREEN, # Bright Green
|
|
369
|
+
93: curses.COLOR_YELLOW, # Bright Yellow
|
|
370
|
+
94: curses.COLOR_BLUE, # Bright Blue
|
|
371
|
+
95: curses.COLOR_MAGENTA, # Bright Magenta
|
|
372
|
+
96: curses.COLOR_CYAN, # Bright Cyan
|
|
373
|
+
97: curses.COLOR_WHITE # Bright White
|
|
374
|
+
}
|
|
345
375
|
# ------------ Exportable Help Functions ----------------
|
|
346
376
|
# check if command sshpass is available
|
|
347
377
|
_binPaths = {}
|
|
@@ -998,9 +1028,6 @@ def __expand_hostnames(hosts) -> dict:
|
|
|
998
1028
|
username, host = host.split('@',1)
|
|
999
1029
|
username = username.strip()
|
|
1000
1030
|
host = host.strip()
|
|
1001
|
-
username, host = host.split('@',1)
|
|
1002
|
-
username = username.strip()
|
|
1003
|
-
host = host.strip()
|
|
1004
1031
|
# first we check if the hostname is an range of IP addresses
|
|
1005
1032
|
# This is done by checking if the hostname follows four fields of
|
|
1006
1033
|
# "(((\d{1,3}|x|\*|\?)(-(\d{1,3}|x|\*|\?))?)|(\[(\d{1,3}|x|\*|\?)(-(\d{1,3}|x|\*|\?))?\]))"
|
|
@@ -1059,10 +1086,10 @@ def __handle_reading_stream(stream,target, host):
|
|
|
1059
1086
|
if not keepLastLine:
|
|
1060
1087
|
target.pop()
|
|
1061
1088
|
host.output.pop()
|
|
1062
|
-
host.printedLines -= 1
|
|
1063
1089
|
current_line_str = current_line.decode('utf-8',errors='backslashreplace')
|
|
1064
1090
|
target.append(current_line_str)
|
|
1065
1091
|
host.output.append(current_line_str)
|
|
1092
|
+
host.lineNumToReprintSet.add(len(host.output)-1)
|
|
1066
1093
|
host.lastUpdateTime = time.time()
|
|
1067
1094
|
current_line = bytearray()
|
|
1068
1095
|
lastLineCommited = True
|
|
@@ -1329,12 +1356,13 @@ def run_command(host, sem, timeout=60,passwds=None):
|
|
|
1329
1356
|
# remove last line if it is a countdown
|
|
1330
1357
|
if host.output and timeoutLineAppended and host.output[-1].strip().endswith('] seconds!') and host.output[-1].strip().startswith('Timeout in ['):
|
|
1331
1358
|
host.output.pop()
|
|
1332
|
-
host.printedLines -= 1
|
|
1333
1359
|
host.output.append(timeoutLine)
|
|
1360
|
+
host.lineNumToReprintSet.add(len(host.output)-1)
|
|
1334
1361
|
timeoutLineAppended = True
|
|
1335
1362
|
elif host.output and timeoutLineAppended and host.output[-1].strip().endswith('] seconds!') and host.output[-1].strip().startswith('Timeout in ['):
|
|
1336
1363
|
host.output.pop()
|
|
1337
|
-
host.
|
|
1364
|
+
host.output.append('')
|
|
1365
|
+
host.lineNumToReprintSet.add(len(host.output)-1)
|
|
1338
1366
|
timeoutLineAppended = False
|
|
1339
1367
|
if _emo:
|
|
1340
1368
|
host.stderr.append('Ctrl C detected, Emergency Stop!')
|
|
@@ -1424,6 +1452,302 @@ def start_run_on_hosts(hosts, timeout=60,password=None,max_connections=4 * os.cp
|
|
|
1424
1452
|
return threads
|
|
1425
1453
|
|
|
1426
1454
|
# ------------ Display Block ----------------
|
|
1455
|
+
def __approximate_color_8bit(color):
|
|
1456
|
+
"""
|
|
1457
|
+
Approximate an 8-bit color (0-255) to the nearest curses color.
|
|
1458
|
+
|
|
1459
|
+
Args:
|
|
1460
|
+
color: 8-bit color code
|
|
1461
|
+
|
|
1462
|
+
Returns:
|
|
1463
|
+
Curses color code
|
|
1464
|
+
"""
|
|
1465
|
+
if color < 8: # Standard and bright colors
|
|
1466
|
+
return ANSI_TO_CURSES_COLOR.get(color % 8 + 30, curses.COLOR_WHITE)
|
|
1467
|
+
elif 8 <= color < 16: # Bright colors
|
|
1468
|
+
return ANSI_TO_CURSES_COLOR.get(color % 8 + 90, curses.COLOR_WHITE)
|
|
1469
|
+
elif 16 <= color <= 231: # Color cube
|
|
1470
|
+
# Convert 216-color cube index to RGB
|
|
1471
|
+
color -= 16
|
|
1472
|
+
r = (color // 36) % 6 * 51
|
|
1473
|
+
g = (color // 6) % 6 * 51
|
|
1474
|
+
b = color % 6 * 51
|
|
1475
|
+
return __approximate_color_24bit(r, g, b) # Map to the closest curses color
|
|
1476
|
+
elif 232 <= color <= 255: # Grayscale
|
|
1477
|
+
gray = (color - 232) * 10 + 8
|
|
1478
|
+
return __approximate_color_24bit(gray, gray, gray)
|
|
1479
|
+
else:
|
|
1480
|
+
return curses.COLOR_WHITE # Fallback to white for unexpected values
|
|
1481
|
+
|
|
1482
|
+
def __approximate_color_24bit(r, g, b):
|
|
1483
|
+
"""
|
|
1484
|
+
Approximate a 24-bit RGB color to the nearest curses color.
|
|
1485
|
+
Will initiate a curses color if curses.can_change_color() is True.
|
|
1486
|
+
|
|
1487
|
+
Globals:
|
|
1488
|
+
__curses_color_table: Dictionary of RGB color to curses color code
|
|
1489
|
+
__curses_current_color_index: Current index of the
|
|
1490
|
+
|
|
1491
|
+
Args:
|
|
1492
|
+
r: Red component (0-255)
|
|
1493
|
+
g: Green component (0-255)
|
|
1494
|
+
b: Blue component (0-255)
|
|
1495
|
+
|
|
1496
|
+
Returns:
|
|
1497
|
+
Curses color code
|
|
1498
|
+
"""
|
|
1499
|
+
if curses.can_change_color():
|
|
1500
|
+
global __curses_color_table,__curses_current_color_index
|
|
1501
|
+
# Initiate a new color if it does not exist
|
|
1502
|
+
if (r, g, b) not in __curses_color_table:
|
|
1503
|
+
if __curses_current_color_index >= curses.COLORS:
|
|
1504
|
+
eprint("Warning: Maximum number of colors reached. Wrapping around.")
|
|
1505
|
+
__curses_current_color_index = 10
|
|
1506
|
+
curses.init_color(__curses_current_color_index, int(r/255*1000), int(g/255*1000), int(b/255*1000))
|
|
1507
|
+
__curses_color_table[(r, g, b)] = __curses_current_color_index
|
|
1508
|
+
__curses_current_color_index += 1
|
|
1509
|
+
return __curses_color_table[(r, g, b)]
|
|
1510
|
+
# Fallback to 8-bit color approximation
|
|
1511
|
+
colors = {
|
|
1512
|
+
curses.COLOR_BLACK: (0, 0, 0),
|
|
1513
|
+
curses.COLOR_RED: (255, 0, 0),
|
|
1514
|
+
curses.COLOR_GREEN: (0, 255, 0),
|
|
1515
|
+
curses.COLOR_YELLOW: (255, 255, 0),
|
|
1516
|
+
curses.COLOR_BLUE: (0, 0, 255),
|
|
1517
|
+
curses.COLOR_MAGENTA: (255, 0, 255),
|
|
1518
|
+
curses.COLOR_CYAN: (0, 255, 255),
|
|
1519
|
+
curses.COLOR_WHITE: (255, 255, 255),
|
|
1520
|
+
}
|
|
1521
|
+
best_match = curses.COLOR_WHITE
|
|
1522
|
+
min_distance = float("inf")
|
|
1523
|
+
for color, (cr, cg, cb) in colors.items():
|
|
1524
|
+
distance = math.sqrt((r - cr) ** 2 + (g - cg) ** 2 + (b - cb) ** 2)
|
|
1525
|
+
if distance < min_distance:
|
|
1526
|
+
min_distance = distance
|
|
1527
|
+
best_match = color
|
|
1528
|
+
return best_match
|
|
1529
|
+
|
|
1530
|
+
def __get_curses_color_pair(fg, bg):
|
|
1531
|
+
"""
|
|
1532
|
+
Use curses color int values to create a curses color pair.
|
|
1533
|
+
|
|
1534
|
+
Globals:
|
|
1535
|
+
__curses_global_color_pairs: Dictionary of color pairs
|
|
1536
|
+
__curses_current_color_pair_index: Current index of the color pair
|
|
1537
|
+
|
|
1538
|
+
Args:
|
|
1539
|
+
fg: Foreground color code
|
|
1540
|
+
bg: Background color code
|
|
1541
|
+
|
|
1542
|
+
Returns:
|
|
1543
|
+
Curses color pair code
|
|
1544
|
+
"""
|
|
1545
|
+
global __curses_global_color_pairs, __curses_current_color_pair_index
|
|
1546
|
+
if (fg, bg) not in __curses_global_color_pairs:
|
|
1547
|
+
if __curses_current_color_pair_index >= curses.COLOR_PAIRS:
|
|
1548
|
+
print("Warning: Maximum number of color pairs reached, wrapping around.")
|
|
1549
|
+
__curses_current_color_pair_index = 1
|
|
1550
|
+
curses.init_pair(__curses_current_color_pair_index, fg, bg)
|
|
1551
|
+
__curses_global_color_pairs[(fg, bg)] = __curses_current_color_pair_index
|
|
1552
|
+
__curses_current_color_pair_index += 1
|
|
1553
|
+
return curses.color_pair(__curses_global_color_pairs[(fg, bg)])
|
|
1554
|
+
|
|
1555
|
+
def __parse_ansi_escape_sequence_to_curses_attr(escape_code,color_pair_list = None):
|
|
1556
|
+
"""
|
|
1557
|
+
Parse ANSI escape codes to extract foreground and background colors.
|
|
1558
|
+
|
|
1559
|
+
Args:
|
|
1560
|
+
escape_code: ANSI escape sequence for color
|
|
1561
|
+
color_pair_list: List of [foreground, background, color_pair] curses color pair values
|
|
1562
|
+
|
|
1563
|
+
Returns:
|
|
1564
|
+
Curses color pair / attribute code
|
|
1565
|
+
"""
|
|
1566
|
+
if not escape_code:
|
|
1567
|
+
return 1
|
|
1568
|
+
if not color_pair_list:
|
|
1569
|
+
color_pair_list = [-1,-1,1]
|
|
1570
|
+
color_match = escape_code.lstrip("\x1b[").rstrip("m").split(";")
|
|
1571
|
+
color_match = [x if x else '0' for x in color_match] # Replace empty strings with '0' (reset)
|
|
1572
|
+
if color_match:
|
|
1573
|
+
processed_index = -1
|
|
1574
|
+
for i, param in enumerate(color_match):
|
|
1575
|
+
if processed_index >= i:
|
|
1576
|
+
# if the index has been processed, skip
|
|
1577
|
+
continue
|
|
1578
|
+
if param.isdigit():
|
|
1579
|
+
if int(param) == 0:
|
|
1580
|
+
color_pair_list[0] = -1
|
|
1581
|
+
color_pair_list[1] = -1
|
|
1582
|
+
color_pair_list[2] = 1
|
|
1583
|
+
elif int(param) == 38:
|
|
1584
|
+
if i + 1 >= len(color_match):
|
|
1585
|
+
# Invalid color code, skip
|
|
1586
|
+
continue
|
|
1587
|
+
if color_match[i + 1] == "5":
|
|
1588
|
+
# 8-bit foreground color
|
|
1589
|
+
if i + 2 >= len(color_match) or not color_match[i + 2].isdigit():
|
|
1590
|
+
# Invalid color code, skip
|
|
1591
|
+
processed_index = i + 1
|
|
1592
|
+
continue
|
|
1593
|
+
color_pair_list[0] = __approximate_color_8bit(int(color_match[i + 2]))
|
|
1594
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1595
|
+
processed_index = i + 2
|
|
1596
|
+
elif color_match[i + 1] == "2":
|
|
1597
|
+
# 24-bit foreground color
|
|
1598
|
+
if i + 4 >= len(color_match) or not all(x.isdigit() for x in color_match[i + 2:i + 5]):
|
|
1599
|
+
# Invalid color code, skip
|
|
1600
|
+
processed_index = i + 1
|
|
1601
|
+
continue
|
|
1602
|
+
color_pair_list[0] = __approximate_color_24bit(int(color_match[i + 2]), int(color_match[i + 3]), int(color_match[i + 4]))
|
|
1603
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1604
|
+
processed_index = i + 4
|
|
1605
|
+
elif int(param) == 48:
|
|
1606
|
+
if i + 1 >= len(color_match):
|
|
1607
|
+
# Invalid color code, skip
|
|
1608
|
+
continue
|
|
1609
|
+
if color_match[i + 1] == "5":
|
|
1610
|
+
# 8-bit background color
|
|
1611
|
+
if i + 2 >= len(color_match) or not color_match[i + 2].isdigit():
|
|
1612
|
+
# Invalid color code, skip
|
|
1613
|
+
processed_index = i + 1
|
|
1614
|
+
continue
|
|
1615
|
+
color_pair_list[1] = __approximate_color_8bit(int(color_match[i + 2]))
|
|
1616
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1617
|
+
processed_index = i + 2
|
|
1618
|
+
elif color_match[i + 1] == "2":
|
|
1619
|
+
# 24-bit background color
|
|
1620
|
+
if i + 4 >= len(color_match) or not all(x.isdigit() for x in color_match[i + 2:i + 5]):
|
|
1621
|
+
# Invalid color code, skip
|
|
1622
|
+
processed_index = i + 1
|
|
1623
|
+
continue
|
|
1624
|
+
color_pair_list[1] = __approximate_color_24bit(int(color_match[i + 2]), int(color_match[i + 3]), int(color_match[i + 4]))
|
|
1625
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1626
|
+
processed_index = i + 4
|
|
1627
|
+
elif 30 <= int(param) <= 37 or 90 <= int(param) <= 97:
|
|
1628
|
+
# 4-bit foreground color
|
|
1629
|
+
color_pair_list[0] = ANSI_TO_CURSES_COLOR.get(int(param), curses.COLOR_WHITE)
|
|
1630
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1631
|
+
elif 40 <= int(param) <= 47 or 100 <= int(param) <= 107:
|
|
1632
|
+
# 4-bit background color
|
|
1633
|
+
color_pair_list[1] = ANSI_TO_CURSES_COLOR.get(int(param)-10, curses.COLOR_BLACK)
|
|
1634
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1635
|
+
elif int(param) == 1:
|
|
1636
|
+
color_pair_list[2] = color_pair_list[2] | curses.A_BOLD
|
|
1637
|
+
elif int(param) == 2:
|
|
1638
|
+
color_pair_list[2] = color_pair_list[2] | curses.A_DIM
|
|
1639
|
+
elif int(param) == 4:
|
|
1640
|
+
color_pair_list[2] = color_pair_list[2] | curses.A_UNDERLINE
|
|
1641
|
+
elif int(param) == 5:
|
|
1642
|
+
color_pair_list[2] = color_pair_list[2] | curses.A_BLINK
|
|
1643
|
+
elif int(param) == 7:
|
|
1644
|
+
color_pair_list[2] = color_pair_list[2] | curses.A_REVERSE
|
|
1645
|
+
elif int(param) == 8:
|
|
1646
|
+
color_pair_list[2] = color_pair_list[2] | curses.A_INVIS
|
|
1647
|
+
elif int(param) == 21:
|
|
1648
|
+
color_pair_list[2] = color_pair_list[2] & ~curses.A_BOLD
|
|
1649
|
+
elif int(param) == 22:
|
|
1650
|
+
color_pair_list[2] = color_pair_list[2] & ~curses.A_DIM
|
|
1651
|
+
elif int(param) == 24:
|
|
1652
|
+
color_pair_list[2] = color_pair_list[2] & ~curses.A_UNDERLINE
|
|
1653
|
+
elif int(param) == 25:
|
|
1654
|
+
color_pair_list[2] = color_pair_list[2] & ~curses.A_BLINK
|
|
1655
|
+
elif int(param) == 27:
|
|
1656
|
+
color_pair_list[2] = color_pair_list[2] & ~curses.A_REVERSE
|
|
1657
|
+
elif int(param) == 28:
|
|
1658
|
+
color_pair_list[2] = color_pair_list[2] & ~curses.A_INVIS
|
|
1659
|
+
elif int(param) == 39:
|
|
1660
|
+
color_pair_list[0] = -1
|
|
1661
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1662
|
+
elif int(param) == 49:
|
|
1663
|
+
color_pair_list[1] = -1
|
|
1664
|
+
color_pair_list[2] = __get_curses_color_pair(color_pair_list[0], color_pair_list[1])
|
|
1665
|
+
else:
|
|
1666
|
+
color_pair_list[0] = -1
|
|
1667
|
+
color_pair_list[1] = -1
|
|
1668
|
+
color_pair_list[2] = 1
|
|
1669
|
+
return color_pair_list[2]
|
|
1670
|
+
|
|
1671
|
+
def _curses_add_string_to_window(window, line = '', y = 0, x = 0, number_of_char_to_write = -1, color_pair_list = [-1,-1,1],fill_char=' ',parse_ansi_colors = True,centered = False,lead_str = '', trail_str = '',box_ansi_color = None, keep_top_n_lines = 0):
|
|
1672
|
+
"""
|
|
1673
|
+
Add a string to a curses window with / without ANSI color escape sequences translated to curses color pairs.
|
|
1674
|
+
|
|
1675
|
+
Args:
|
|
1676
|
+
window: curses window object
|
|
1677
|
+
line: The line to add
|
|
1678
|
+
y: Line position in the window. Use -1 to scroll the window up 1 line and add the line at the bottom
|
|
1679
|
+
x: Column position in the window
|
|
1680
|
+
number_of_char_to_write: Number of characters to write. -1 for all remaining space in line, 0 for no characters, and a positive integer for a specific number of characters.
|
|
1681
|
+
color_pair_list: List of [foreground, background, color_pair] curses color pair values
|
|
1682
|
+
fill_char: Character to fill the remaining space in the line
|
|
1683
|
+
parse_ansi_colors: Parse ASCII color codes
|
|
1684
|
+
centered: Center the text in the window
|
|
1685
|
+
lead_str: Leading string to add to the line
|
|
1686
|
+
trail_str: Trailing string to add to the line
|
|
1687
|
+
box_ansi_color: ANSI color escape sequence for the box color
|
|
1688
|
+
keep_top_n_lines: Number of lines to keep at the top of the window
|
|
1689
|
+
|
|
1690
|
+
Returns:
|
|
1691
|
+
None
|
|
1692
|
+
"""
|
|
1693
|
+
if window.getmaxyx()[0] == 0 or window.getmaxyx()[1] == 0 or x >= window.getmaxyx()[1]:
|
|
1694
|
+
return
|
|
1695
|
+
if x < 0:
|
|
1696
|
+
x = window.getmaxyx()[1] + x
|
|
1697
|
+
if number_of_char_to_write == -1:
|
|
1698
|
+
numChar = window.getmaxyx()[1] - x -1
|
|
1699
|
+
elif number_of_char_to_write == 0:
|
|
1700
|
+
return
|
|
1701
|
+
elif number_of_char_to_write + x > window.getmaxyx()[1]:
|
|
1702
|
+
numChar = window.getmaxyx()[1] - x -1
|
|
1703
|
+
else:
|
|
1704
|
+
numChar = number_of_char_to_write
|
|
1705
|
+
if numChar < 0:
|
|
1706
|
+
return
|
|
1707
|
+
if y < 0 or y >= window.getmaxyx()[0]:
|
|
1708
|
+
if keep_top_n_lines > window.getmaxyx()[0] -1:
|
|
1709
|
+
keep_top_n_lines = window.getmaxyx()[0] -1
|
|
1710
|
+
if keep_top_n_lines < 0:
|
|
1711
|
+
keep_top_n_lines = 0
|
|
1712
|
+
window.move(keep_top_n_lines,0)
|
|
1713
|
+
window.deleteln()
|
|
1714
|
+
y = window.getmaxyx()[0] - 1
|
|
1715
|
+
line = line.replace('\n', ' ').replace('\r', ' ')
|
|
1716
|
+
if parse_ansi_colors:
|
|
1717
|
+
segments = re.split(r"(\x1b\[[\d;]*m)", line) # Split line by ANSI escape codes
|
|
1718
|
+
else:
|
|
1719
|
+
segments = [line]
|
|
1720
|
+
charsWritten = 0
|
|
1721
|
+
boxAttr = __parse_ansi_escape_sequence_to_curses_attr(box_ansi_color)
|
|
1722
|
+
# first add the lead_str
|
|
1723
|
+
window.addnstr(y, x, lead_str, numChar, boxAttr)
|
|
1724
|
+
charsWritten = min(len(lead_str), numChar)
|
|
1725
|
+
# process centering
|
|
1726
|
+
if centered:
|
|
1727
|
+
fill_length = numChar - len(lead_str) - len(trail_str) - sum([len(segment) for segment in segments if not segment.startswith("\x1b[")])
|
|
1728
|
+
window.addnstr(y, x + charsWritten, fill_char * (fill_length // 2), numChar - charsWritten, boxAttr)
|
|
1729
|
+
charsWritten += min(fill_length // 2, numChar - charsWritten)
|
|
1730
|
+
# add the segments
|
|
1731
|
+
for segment in segments:
|
|
1732
|
+
if not segment:
|
|
1733
|
+
continue
|
|
1734
|
+
if parse_ansi_colors and segment.startswith("\x1b["):
|
|
1735
|
+
# Parse ANSI escape sequence
|
|
1736
|
+
newAttr = __parse_ansi_escape_sequence_to_curses_attr(segment,color_pair_list)
|
|
1737
|
+
else:
|
|
1738
|
+
# Add text with current color
|
|
1739
|
+
if charsWritten < numChar:
|
|
1740
|
+
window.addnstr(y, x + charsWritten, segment, numChar - charsWritten, color_pair_list[2])
|
|
1741
|
+
charsWritten += min(len(segment), numChar - charsWritten)
|
|
1742
|
+
# if we have finished printing segments but we still have space, we will fill it with fill_char
|
|
1743
|
+
if charsWritten + len(trail_str) < numChar:
|
|
1744
|
+
fillStr = fill_char * (numChar - charsWritten - len(trail_str))
|
|
1745
|
+
#fillStr = f'{color_pair_list}'
|
|
1746
|
+
window.addnstr(y, x + charsWritten, fillStr + trail_str, numChar - charsWritten, boxAttr)
|
|
1747
|
+
charsWritten += numChar - charsWritten
|
|
1748
|
+
else:
|
|
1749
|
+
window.addnstr(y, x + charsWritten, trail_str, numChar - charsWritten, boxAttr)
|
|
1750
|
+
|
|
1427
1751
|
def _get_hosts_to_display (hosts, max_num_hosts, hosts_to_display = None):
|
|
1428
1752
|
'''
|
|
1429
1753
|
Generate a list for the hosts to be displayed on the screen. This is used to display as much relevant information as possible.
|
|
@@ -1458,6 +1782,7 @@ def _get_hosts_to_display (hosts, max_num_hosts, hosts_to_display = None):
|
|
|
1458
1782
|
|
|
1459
1783
|
def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min_char_len = DEFAULT_CURSES_MINIMUM_CHAR_LEN, min_line_len = DEFAULT_CURSES_MINIMUM_LINE_LEN,single_window=DEFAULT_SINGLE_WINDOW, config_reason= 'New Configuration'):
|
|
1460
1784
|
try:
|
|
1785
|
+
box_ansi_color = None
|
|
1461
1786
|
org_dim = stdscr.getmaxyx()
|
|
1462
1787
|
new_configured = True
|
|
1463
1788
|
# To do this, first we need to know the size of the terminal
|
|
@@ -1510,7 +1835,6 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
1510
1835
|
|
|
1511
1836
|
old_stat = ''
|
|
1512
1837
|
old_bottom_stat = ''
|
|
1513
|
-
old_cursor_position = -1
|
|
1514
1838
|
# we refresh the screen every 0.1 seconds
|
|
1515
1839
|
last_refresh_time = time.perf_counter()
|
|
1516
1840
|
stdscr.clear()
|
|
@@ -1519,6 +1843,7 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
1519
1843
|
stdscr.nodelay(True)
|
|
1520
1844
|
# we generate a stats window at the top of the screen
|
|
1521
1845
|
stat_window = curses.newwin(1, max_x, 0, 0)
|
|
1846
|
+
stat_window.leaveok(True)
|
|
1522
1847
|
# We create a window for each host
|
|
1523
1848
|
host_windows = []
|
|
1524
1849
|
for i, host in enumerate(hosts_to_display):
|
|
@@ -1529,13 +1854,18 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
1529
1854
|
#print(f"Creating a window at {y},{x}")
|
|
1530
1855
|
# We create the window
|
|
1531
1856
|
host_window = curses.newwin(host_window_height, host_window_width, y, x)
|
|
1857
|
+
host_window.idlok(True)
|
|
1858
|
+
host_window.scrollok(True)
|
|
1859
|
+
host_window.leaveok(True)
|
|
1532
1860
|
host_windows.append(host_window)
|
|
1533
1861
|
# If there is space left, we will draw the bottom border
|
|
1534
1862
|
bottom_border = None
|
|
1535
1863
|
if y + host_window_height < org_dim[0]:
|
|
1536
1864
|
bottom_border = curses.newwin(1, max_x, y + host_window_height, 0)
|
|
1865
|
+
bottom_border.leaveok(True)
|
|
1537
1866
|
#bottom_border.clear()
|
|
1538
|
-
bottom_border.
|
|
1867
|
+
#bottom_border.addnstr(0, 0, '-' * (max_x - 1), max_x - 1)
|
|
1868
|
+
_curses_add_string_to_window(window=bottom_border, y=0, line='-' * (max_x - 1),fill_char='-',box_ansi_color=box_ansi_color)
|
|
1539
1869
|
bottom_border.refresh()
|
|
1540
1870
|
while host_stats['running'] > 0 or host_stats['waiting'] > 0:
|
|
1541
1871
|
# Check for keypress
|
|
@@ -1626,38 +1956,40 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
1626
1956
|
return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'Terminal resize detected')
|
|
1627
1957
|
# We generate the aggregated stats if user did not input anything
|
|
1628
1958
|
if not __keyPressesIn[lineToDisplay]:
|
|
1629
|
-
stats = '┍'+ f" Total: {len(hosts)} Running: {host_stats['running']} Failed: {host_stats['failed']} Finished: {host_stats['finished']} Waiting: {host_stats['waiting']} ww: {min_char_len} wh:{min_line_len} "[:max_x - 2].center(max_x - 2, "━")
|
|
1959
|
+
#stats = '┍'+ f" Total: {len(hosts)} Running: {host_stats['running']} Failed: {host_stats['failed']} Finished: {host_stats['finished']} Waiting: {host_stats['waiting']} ww: {min_char_len} wh:{min_line_len} "[:max_x - 2].center(max_x - 2, "━")
|
|
1960
|
+
stats = f"Total: {len(hosts)} Running: {host_stats['running']} Failed: {host_stats['failed']} Finished: {host_stats['finished']} Waiting: {host_stats['waiting']} ww: {min_char_len} wh:{min_line_len} "
|
|
1630
1961
|
else:
|
|
1631
1962
|
# we use the stat bar to display the key presses
|
|
1632
1963
|
encodedLine = ''.join(__keyPressesIn[lineToDisplay]).encode().decode().strip('\n') + ' '
|
|
1633
|
-
#
|
|
1634
|
-
|
|
1635
|
-
#
|
|
1964
|
+
#stats = '┍'+ f"Send CMD: {encodedLine}"[:max_x - 2].center(max_x - 2, "━")
|
|
1965
|
+
stats = f"Send CMD: {encodedLine}"
|
|
1966
|
+
# format the stats line with chracter at curser position inverted using ansi escape sequence
|
|
1967
|
+
stats = f'{stats[:curserPosition]}\x1b[7m{stats[curserPosition]}\x1b[0m{stats[curserPosition + 1:]}'
|
|
1968
|
+
if stats != old_stat :
|
|
1969
|
+
old_stat = stats
|
|
1970
|
+
# calculate the real curser position in stats as we centered the stats
|
|
1971
|
+
# if 'Send CMD: ' in stats:
|
|
1972
|
+
# curserPositionStats = min(min(curserPosition,len(encodedLine) -1) + stats.find('Send CMD: ')+len('Send CMD: '), max_x -2)
|
|
1636
1973
|
# else:
|
|
1637
|
-
#
|
|
1638
|
-
|
|
1974
|
+
# curserPositionStats = max_x -2
|
|
1975
|
+
#stat_window.clear()
|
|
1976
|
+
#stat_window.addstr(0, 0, stats)
|
|
1977
|
+
# add the line with curser that inverses the color at the curser position
|
|
1978
|
+
# stat_window.addstr(0, 0, stats[:curserPositionStats])
|
|
1979
|
+
# stat_window.addch(0,curserPositionStats, stats[curserPositionStats], curses.A_REVERSE)
|
|
1980
|
+
# stat_window.addnstr(0, curserPositionStats + 1, stats[curserPositionStats + 1:], max_x - 1 - curserPositionStats)
|
|
1981
|
+
# stat_window.refresh()
|
|
1982
|
+
_curses_add_string_to_window(window=stat_window, y=0, line=stats, color_pair_list=[-1, -1, 1],centered=True,fill_char='━',lead_str='┍',box_ansi_color=box_ansi_color)
|
|
1639
1983
|
if bottom_border:
|
|
1640
|
-
|
|
1984
|
+
#target_length = max_x - 2 + len('\x1b[33m\x1b[0m\x1b[31m\x1b[0m\x1b[32m\x1b[0m')
|
|
1985
|
+
#bottom_stats = '└'+ f" Total: {len(hosts)} Running: \x1b[33m{host_stats['running']}\x1b[0m Failed: \x1b[31m{host_stats['failed']}\x1b[0m Finished: \x1b[32m{host_stats['finished']}\x1b[0m Waiting: {host_stats['waiting']} "[:target_length].center(target_length, "─")
|
|
1986
|
+
bottom_stats = f" Total: {len(hosts)} Running: \x1b[33m{host_stats['running']}\x1b[0m Failed: \x1b[31m{host_stats['failed']}\x1b[0m Finished: \x1b[32m{host_stats['finished']}\x1b[0m Waiting: {host_stats['waiting']} "
|
|
1641
1987
|
if bottom_stats != old_bottom_stat:
|
|
1642
1988
|
old_bottom_stat = bottom_stats
|
|
1643
1989
|
#bottom_border.clear()
|
|
1644
|
-
bottom_border.
|
|
1990
|
+
#bottom_border.addnstr(0, 0, bottom_stats, max_x - 1)
|
|
1991
|
+
_curses_add_string_to_window(window=bottom_border, y=0, line=bottom_stats,fill_char='─',centered=True,lead_str='└',box_ansi_color=box_ansi_color)
|
|
1645
1992
|
bottom_border.refresh()
|
|
1646
|
-
if stats != old_stat or curserPosition != old_cursor_position:
|
|
1647
|
-
old_stat = stats
|
|
1648
|
-
old_cursor_position = curserPosition
|
|
1649
|
-
# calculate the real curser position in stats as we centered the stats
|
|
1650
|
-
if 'Send CMD: ' in stats:
|
|
1651
|
-
curserPositionStats = min(min(curserPosition,len(encodedLine) -1) + stats.find('Send CMD: ')+len('Send CMD: '), max_x -2)
|
|
1652
|
-
else:
|
|
1653
|
-
curserPositionStats = max_x -2
|
|
1654
|
-
#stat_window.clear()
|
|
1655
|
-
#stat_window.addstr(0, 0, stats)
|
|
1656
|
-
# add the line with curser that inverses the color at the curser position
|
|
1657
|
-
stat_window.addstr(0, 0, stats[:curserPositionStats], curses.color_pair(1))
|
|
1658
|
-
stat_window.addstr(0, curserPositionStats, stats[curserPositionStats], curses.color_pair(2))
|
|
1659
|
-
stat_window.addstr(0, curserPositionStats + 1, stats[curserPositionStats + 1:], curses.color_pair(1))
|
|
1660
|
-
stat_window.refresh()
|
|
1661
1993
|
# set the maximum refresh rate to 100 Hz
|
|
1662
1994
|
if time.perf_counter() - last_refresh_time < 0.01:
|
|
1663
1995
|
time.sleep(max(0,0.01 - time.perf_counter() + last_refresh_time))
|
|
@@ -1669,18 +2001,27 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
1669
2001
|
try:
|
|
1670
2002
|
#host_window.clear()
|
|
1671
2003
|
# we will try to center the name of the host with ┼ at the beginning and end and ─ in between
|
|
1672
|
-
linePrintOut = f'┼{(host.name+":["+host.command+"]")[:host_window_width - 2].center(host_window_width - 1, "─")}'.replace('\n', ' ').replace('\r', ' ').strip()
|
|
1673
|
-
|
|
2004
|
+
#linePrintOut = f'┼{(host.name+":["+host.command+"]")[:host_window_width - 2].center(host_window_width - 1, "─")}'.replace('\n', ' ').replace('\r', ' ').strip()
|
|
2005
|
+
#linePrintOut = f'┼{(host.name+":["+host.command+"]")[:host_window_width - 2].center(host_window_width - 1, "─")}'.replace('\n', ' ').replace('\r', ' ').strip()
|
|
2006
|
+
#host_window.addnstr(0, 0, linePrintOut, host_window_width - 1)
|
|
2007
|
+
linePrintOut = f'{host.name}:[{host.command}]'.replace('\n', ' ').replace('\r', ' ').strip()
|
|
2008
|
+
_curses_add_string_to_window(window=host_window, y=0, line=linePrintOut, color_pair_list=[-1, -1, 1],centered=True,fill_char='─',lead_str='┼',box_ansi_color=box_ansi_color)
|
|
2009
|
+
#_add_line_with_ansi_colors(window=host_window, y=0, x=0, line=linePrintOut, n=host_window_width - 1, color_pair_list = host.current_color_pair)
|
|
1674
2010
|
# we will display the latest outputs of the host as much as we can
|
|
1675
|
-
for i, line in enumerate(host.output[-(host_window_height - 1):]):
|
|
2011
|
+
#for i, line in enumerate(host.output[-(host_window_height - 1):]):
|
|
1676
2012
|
# print(f"Printng a line at {i + 1} with length of {len('│'+line[:host_window_width - 1])}")
|
|
1677
2013
|
# time.sleep(10)
|
|
1678
|
-
linePrintOut = ('│'+line[:host_window_width - 2].replace('\n', ' ').replace('\r', ' ')).strip()
|
|
1679
|
-
host_window.
|
|
2014
|
+
#linePrintOut = ('│'+line[:host_window_width - 2].replace('\n', ' ').replace('\r', ' ')).strip()
|
|
2015
|
+
#host_window.addnstr(i + 1, 0, linePrintOut, host_window_width - 1)
|
|
2016
|
+
#_curses_add_string_to_window(window=host_window, y=i + 1, line=line, color_pair_list=host.current_color_pair,lead_str='│')
|
|
1680
2017
|
# we draw the rest of the available lines
|
|
2018
|
+
# for i in range(len(host.output), host_window_height - 1):
|
|
2019
|
+
# # print(f"Printng a line at {i + 1} with length of {len('│')}")
|
|
2020
|
+
# host_window.addnstr(i + 1, 0, '│'.ljust(host_window_width - 1, ' '), host_window_width - 1)
|
|
2021
|
+
for i in range(host.printedLines, len(host.output)):
|
|
2022
|
+
_curses_add_string_to_window(window=host_window, y=i + 1, line=host.output[i], color_pair_list=host.current_color_pair,lead_str='│',keep_top_n_lines=1,box_ansi_color=box_ansi_color)
|
|
1681
2023
|
for i in range(len(host.output), host_window_height - 1):
|
|
1682
|
-
|
|
1683
|
-
host_window.addstr(i + 1, 0, '│'.ljust(host_window_width - 1, ' '))
|
|
2024
|
+
_curses_add_string_to_window(window=host_window, y=i + 1,lead_str='│',keep_top_n_lines=1,box_ansi_color=box_ansi_color)
|
|
1684
2025
|
host.printedLines = len(host.output)
|
|
1685
2026
|
host_window.refresh()
|
|
1686
2027
|
except Exception as e:
|
|
@@ -1689,6 +2030,24 @@ def __generate_display(stdscr, hosts, lineToDisplay = -1,curserPosition = 0, min
|
|
|
1689
2030
|
# print(traceback.format_exc().strip())
|
|
1690
2031
|
if org_dim != stdscr.getmaxyx():
|
|
1691
2032
|
return (lineToDisplay,curserPosition , min_char_len, min_line_len, single_window, 'Terminal resize detected')
|
|
2033
|
+
if host.lineNumToReprintSet:
|
|
2034
|
+
# visible range is from host.printedLines - host_window_height + 1 to host.printedLines
|
|
2035
|
+
visibleLowerBound = host.printedLines - host_window_height + 1
|
|
2036
|
+
for lineNumToReprint in host.lineNumToReprintSet:
|
|
2037
|
+
# if the line is visible, we will reprint it
|
|
2038
|
+
if visibleLowerBound <= lineNumToReprint <= host.printedLines:
|
|
2039
|
+
if visibleLowerBound <= 0:
|
|
2040
|
+
# this means all lines are visible
|
|
2041
|
+
linePos = lineNumToReprint
|
|
2042
|
+
else:
|
|
2043
|
+
# calculate the position of the line to reprint
|
|
2044
|
+
linePos = lineNumToReprint - visibleLowerBound
|
|
2045
|
+
# Note: color can be incorrect if repainting an old line with new colors already initialized,
|
|
2046
|
+
# Thus we will not use any presistent color pair for old lines
|
|
2047
|
+
cpl = host.current_color_pair if lineNumToReprint == host.printedLines else [-1,-1,1]
|
|
2048
|
+
_curses_add_string_to_window(window=host_window, y=linePos + 1, line=host.output[lineNumToReprint], color_pair_list=cpl,lead_str='│',keep_top_n_lines=1,box_ansi_color=box_ansi_color)
|
|
2049
|
+
host.lineNumToReprintSet = set()
|
|
2050
|
+
host_window.refresh()
|
|
1692
2051
|
new_configured = False
|
|
1693
2052
|
last_refresh_time = time.perf_counter()
|
|
1694
2053
|
except Exception as e:
|
|
@@ -1710,33 +2069,39 @@ def curses_print(stdscr, hosts, threads, min_char_len = DEFAULT_CURSES_MINIMUM_C
|
|
|
1710
2069
|
'''
|
|
1711
2070
|
# We create all the windows we need
|
|
1712
2071
|
# We initialize the color pair
|
|
1713
|
-
curses.start_color()
|
|
1714
2072
|
curses.curs_set(0)
|
|
1715
|
-
curses.
|
|
1716
|
-
curses.
|
|
1717
|
-
curses.init_pair(
|
|
1718
|
-
curses.init_pair(4, curses.COLOR_GREEN, curses.COLOR_BLACK)
|
|
1719
|
-
curses.init_pair(5, curses.COLOR_YELLOW, curses.COLOR_BLACK)
|
|
1720
|
-
curses.init_pair(6, curses.COLOR_BLUE, curses.COLOR_BLACK)
|
|
1721
|
-
curses.init_pair(7, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
|
|
1722
|
-
curses.init_pair(8, curses.COLOR_CYAN, curses.COLOR_BLACK)
|
|
1723
|
-
curses.init_pair(9, curses.COLOR_WHITE, curses.COLOR_RED)
|
|
1724
|
-
curses.init_pair(10, curses.COLOR_WHITE, curses.COLOR_GREEN)
|
|
1725
|
-
curses.init_pair(11, curses.COLOR_WHITE, curses.COLOR_YELLOW)
|
|
1726
|
-
curses.init_pair(12, curses.COLOR_WHITE, curses.COLOR_BLUE)
|
|
1727
|
-
curses.init_pair(13, curses.COLOR_WHITE, curses.COLOR_MAGENTA)
|
|
1728
|
-
curses.init_pair(14, curses.COLOR_WHITE, curses.COLOR_CYAN)
|
|
1729
|
-
curses.init_pair(15, curses.COLOR_BLACK, curses.COLOR_RED)
|
|
1730
|
-
curses.init_pair(16, curses.COLOR_BLACK, curses.COLOR_GREEN)
|
|
1731
|
-
curses.init_pair(17, curses.COLOR_BLACK, curses.COLOR_YELLOW)
|
|
1732
|
-
curses.init_pair(18, curses.COLOR_BLACK, curses.COLOR_BLUE)
|
|
1733
|
-
curses.init_pair(19, curses.COLOR_BLACK, curses.COLOR_MAGENTA)
|
|
1734
|
-
curses.init_pair(20, curses.COLOR_BLACK, curses.COLOR_CYAN)
|
|
1735
|
-
|
|
2073
|
+
curses.start_color()
|
|
2074
|
+
curses.use_default_colors()
|
|
2075
|
+
curses.init_pair(1, -1, -1)
|
|
1736
2076
|
# do not generate display if the output window have a size of zero
|
|
1737
2077
|
if stdscr.getmaxyx()[0] < 2 or stdscr.getmaxyx()[1] < 2:
|
|
1738
2078
|
return
|
|
1739
|
-
|
|
2079
|
+
stdscr.idlok(True)
|
|
2080
|
+
stdscr.scrollok(True)
|
|
2081
|
+
stdscr.leaveok(True)
|
|
2082
|
+
# generate some debug information before display initialization
|
|
2083
|
+
try:
|
|
2084
|
+
stdscr.clear()
|
|
2085
|
+
_curses_add_string_to_window(window=stdscr, y=0, line='Initializing display...', n=stdscr.getmaxyx()[1] - 1)
|
|
2086
|
+
# print the size
|
|
2087
|
+
_curses_add_string_to_window(window=stdscr, y=1, line=f"Terminal size: {stdscr.getmaxyx()}", n=stdscr.getmaxyx()[1] - 1)
|
|
2088
|
+
# print the number of hosts
|
|
2089
|
+
_curses_add_string_to_window(window=stdscr, y=2, line=f"Number of hosts: {len(hosts)}", n=stdscr.getmaxyx()[1] - 1)
|
|
2090
|
+
# print the number of threads
|
|
2091
|
+
_curses_add_string_to_window(window=stdscr, y=3, line=f"Number of threads: {len(threads)}", n=stdscr.getmaxyx()[1] - 1)
|
|
2092
|
+
# print the minimum character length
|
|
2093
|
+
_curses_add_string_to_window(window=stdscr, y=4, line=f"Minimum character length: {min_char_len}", n=stdscr.getmaxyx()[1] - 1)
|
|
2094
|
+
# print the minimum line length
|
|
2095
|
+
_curses_add_string_to_window(window=stdscr, y=5, line=f"Minimum line length: {min_line_len}", n=stdscr.getmaxyx()[1] - 1)
|
|
2096
|
+
# print the single window mode
|
|
2097
|
+
_curses_add_string_to_window(window=stdscr, y=6, line=f"Single window mode: {single_window}", n=stdscr.getmaxyx()[1] - 1)
|
|
2098
|
+
# print COLORS and COLOR_PAIRS count
|
|
2099
|
+
_curses_add_string_to_window(window=stdscr, y=7, line=f"len(COLORS): {curses.COLORS} len(COLOR_PAIRS): {curses.COLOR_PAIRS}", n=stdscr.getmaxyx()[1] - 1)
|
|
2100
|
+
# print if can change color
|
|
2101
|
+
_curses_add_string_to_window(window=stdscr, y=8, line=f"Real color capability: {curses.can_change_color()}", n=stdscr.getmaxyx()[1] - 1)
|
|
2102
|
+
stdscr.refresh()
|
|
2103
|
+
except:
|
|
2104
|
+
pass
|
|
1740
2105
|
params = (-1,0 , min_char_len, min_line_len, single_window,'new config')
|
|
1741
2106
|
while params:
|
|
1742
2107
|
params = __generate_display(stdscr, hosts, *params)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|