2019-08-21 20:01:17 +02:00
|
|
|
#!/usr/bin/env python3
|
2019-08-22 17:31:13 +02:00
|
|
|
import argparse
|
|
|
|
|
import enum
|
2020-03-08 13:52:59 +01:00
|
|
|
import fcntl
|
|
|
|
|
import struct
|
2019-08-21 20:01:17 +02:00
|
|
|
import sys
|
2020-03-08 13:52:59 +01:00
|
|
|
import termios
|
2019-08-21 20:01:17 +02:00
|
|
|
|
|
|
|
|
|
2019-08-22 17:31:13 +02:00
|
|
|
class ColorVariant(enum.IntEnum):
|
|
|
|
|
NONE = enum.auto()
|
|
|
|
|
REGULAR = enum.auto()
|
|
|
|
|
BRIGHT = enum.auto()
|
2020-11-15 14:00:20 +01:00
|
|
|
CUBE = enum.auto()
|
2019-08-22 17:31:13 +02:00
|
|
|
RGB = enum.auto()
|
|
|
|
|
|
|
|
|
|
|
2019-08-21 20:01:17 +02:00
|
|
|
def main():
|
2019-08-22 17:31:13 +02:00
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
|
parser.add_argument(
|
|
|
|
|
'out', type=argparse.FileType(mode='w'), nargs='?', help='name of output file')
|
2020-11-15 13:49:06 +01:00
|
|
|
parser.add_argument('--cols', type=int)
|
|
|
|
|
parser.add_argument('--rows', type=int)
|
2019-08-22 17:31:13 +02:00
|
|
|
parser.add_argument('--colors-regular', action='store_true')
|
|
|
|
|
parser.add_argument('--colors-bright', action='store_true')
|
2020-11-15 14:00:20 +01:00
|
|
|
parser.add_argument('--colors-256', action='store_true')
|
2019-08-22 17:31:13 +02:00
|
|
|
parser.add_argument('--colors-rgb', action='store_true')
|
|
|
|
|
parser.add_argument('--scroll', action='store_true')
|
|
|
|
|
parser.add_argument('--scroll-region', action='store_true')
|
2020-11-16 20:02:53 +01:00
|
|
|
parser.add_argument('--attr-bold', action='store_true')
|
|
|
|
|
parser.add_argument('--attr-italic', action='store_true')
|
|
|
|
|
parser.add_argument('--attr-underline', action='store_true')
|
2020-11-23 19:30:44 +01:00
|
|
|
parser.add_argument('--sixel', action='store_true')
|
2019-08-22 17:31:13 +02:00
|
|
|
|
|
|
|
|
opts = parser.parse_args()
|
|
|
|
|
out = opts.out if opts.out is not None else sys.stdout
|
|
|
|
|
|
2020-11-23 19:30:44 +01:00
|
|
|
lines, cols, height, width = struct.unpack(
|
2020-03-08 13:52:59 +01:00
|
|
|
'HHHH',
|
|
|
|
|
fcntl.ioctl(sys.stdout.fileno(),
|
|
|
|
|
termios.TIOCGWINSZ,
|
|
|
|
|
struct.pack('HHHH', 0, 0, 0, 0)))
|
2019-08-21 20:01:17 +02:00
|
|
|
|
2020-11-15 13:49:06 +01:00
|
|
|
if opts.rows is not None:
|
|
|
|
|
lines = opts.rows
|
|
|
|
|
if opts.cols is not None:
|
|
|
|
|
cols = opts.cols
|
|
|
|
|
|
2019-08-21 20:01:17 +02:00
|
|
|
# Number of characters to write to screen
|
2019-12-19 20:01:52 +01:00
|
|
|
count = 256 * 1024**1
|
2019-08-21 20:01:17 +02:00
|
|
|
|
|
|
|
|
# Characters to choose from
|
2020-11-16 20:22:12 +01:00
|
|
|
alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRTSTUVWXYZ0123456789 öäå 👨👩🧒'
|
2019-08-21 20:01:17 +02:00
|
|
|
|
2019-08-23 21:06:33 +02:00
|
|
|
color_variants = ([ColorVariant.NONE] +
|
2019-08-22 17:31:13 +02:00
|
|
|
([ColorVariant.REGULAR] if opts.colors_regular else []) +
|
|
|
|
|
([ColorVariant.BRIGHT] if opts.colors_bright else []) +
|
2020-11-15 14:00:20 +01:00
|
|
|
([ColorVariant.CUBE] if opts.colors_256 else []) +
|
2019-08-22 17:31:13 +02:00
|
|
|
([ColorVariant.RGB] if opts.colors_rgb else []))
|
|
|
|
|
|
2019-08-21 20:01:17 +02:00
|
|
|
# Enter alt screen
|
2019-08-22 17:31:13 +02:00
|
|
|
out.write('\033[?1049h')
|
|
|
|
|
|
|
|
|
|
with open('/dev/urandom', 'rb') as rand:
|
|
|
|
|
for _ in range(count):
|
|
|
|
|
if opts.scroll and rand.read(1)[0] == 0:
|
|
|
|
|
out.write('\033[m')
|
|
|
|
|
|
|
|
|
|
if opts.scroll_region and rand.read(1)[0] == 0:
|
|
|
|
|
top = rand.read(1)[0] % 3
|
|
|
|
|
bottom = rand.read(1)[0] % 3
|
|
|
|
|
out.write(f'\033[{top};{lines - bottom}r')
|
|
|
|
|
|
2020-11-14 13:46:10 +01:00
|
|
|
lines_to_scroll = rand.read(1)[0] % (lines - 1)
|
2019-08-22 17:31:13 +02:00
|
|
|
rev = rand.read(1)[0] % 2
|
2020-11-16 20:01:58 +01:00
|
|
|
if not rev and rand.read(1)[0] % 2:
|
|
|
|
|
out.write(f'\033[{lines};{cols}H')
|
|
|
|
|
out.write('\n' * lines_to_scroll)
|
|
|
|
|
else:
|
|
|
|
|
out.write(f'\033[{lines_to_scroll + 1}{"T" if rev == 1 else "S"}')
|
2019-08-22 17:31:13 +02:00
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# Generate a random location and a random character
|
|
|
|
|
row = rand.read(1)[0] % lines
|
|
|
|
|
col = rand.read(1)[0] % cols
|
|
|
|
|
c = alphabet[rand.read(1)[0] % len(alphabet)]
|
|
|
|
|
|
2019-08-30 21:37:51 +02:00
|
|
|
repeat = rand.read(1)[0] % (cols - col) + 1
|
|
|
|
|
assert col + repeat <= cols
|
|
|
|
|
|
2019-08-22 17:31:13 +02:00
|
|
|
color_variant = color_variants[rand.read(1)[0] % len(color_variants)]
|
|
|
|
|
|
|
|
|
|
# Position cursor
|
|
|
|
|
out.write(f'\033[{row + 1};{col + 1}H')
|
|
|
|
|
|
|
|
|
|
if color_variant in [ColorVariant.REGULAR, ColorVariant.BRIGHT]:
|
|
|
|
|
do_bg = rand.read(1)[0] % 2
|
|
|
|
|
base = 40 if do_bg else 30
|
|
|
|
|
base += 60 if color_variant == ColorVariant.BRIGHT else 0
|
|
|
|
|
|
|
|
|
|
idx = rand.read(1)[0] % 8
|
|
|
|
|
out.write(f'\033[{base + idx}m')
|
|
|
|
|
|
2020-11-15 14:00:20 +01:00
|
|
|
elif color_variant == ColorVariant.CUBE:
|
|
|
|
|
do_bg = rand.read(1)[0] % 2
|
|
|
|
|
base = 48 if do_bg else 38
|
|
|
|
|
|
|
|
|
|
idx = rand.read(1)[0] % 256
|
|
|
|
|
if rand.read(1)[0] % 2:
|
|
|
|
|
# Old-style
|
|
|
|
|
out.write(f'\033[{base};5;{idx}m')
|
|
|
|
|
else:
|
|
|
|
|
# New-style (sub-parameter based)
|
|
|
|
|
out.write(f'\033[{base}:2:5:{idx}m')
|
|
|
|
|
|
2019-08-22 17:31:13 +02:00
|
|
|
elif color_variant == ColorVariant.RGB:
|
|
|
|
|
do_bg = rand.read(1)[0] % 2
|
2020-11-15 13:59:45 +01:00
|
|
|
base = 48 if do_bg else 38
|
2019-08-22 17:31:13 +02:00
|
|
|
rgb = rand.read(3)
|
2020-11-15 13:59:45 +01:00
|
|
|
|
|
|
|
|
if rand.read(1)[0] % 2:
|
|
|
|
|
# Old-style
|
|
|
|
|
out.write(f'\033[{base};2;{rgb[0]};{rgb[1]};{rgb[2]}m')
|
|
|
|
|
else:
|
|
|
|
|
# New-style (sub-parameter based)
|
|
|
|
|
out.write(f'\033[{base}:2::{rgb[0]}:{rgb[1]}:{rgb[2]}m')
|
2019-08-21 20:01:17 +02:00
|
|
|
|
2020-11-16 20:02:53 +01:00
|
|
|
if opts.attr_bold and rand.read(1)[0] % 5 == 0:
|
|
|
|
|
out.write('\033[1m')
|
|
|
|
|
if opts.attr_italic and rand.read(1)[0] % 5 == 0:
|
|
|
|
|
out.write('\033[3m')
|
|
|
|
|
if opts.attr_underline and rand.read(1)[0] % 5 == 0:
|
|
|
|
|
out.write('\033[4m')
|
|
|
|
|
|
2019-08-30 21:37:51 +02:00
|
|
|
out.write(c * repeat)
|
2019-08-21 20:01:17 +02:00
|
|
|
|
2020-11-16 20:02:38 +01:00
|
|
|
do_sgr_reset = rand.read(1)[0] % 2
|
|
|
|
|
if do_sgr_reset:
|
|
|
|
|
reset_actions = ['\033[m', '\033[39m', '\033[49m']
|
|
|
|
|
idx = rand.read(1)[0] % len(reset_actions)
|
|
|
|
|
out.write(reset_actions[idx])
|
2019-08-21 20:01:17 +02:00
|
|
|
|
|
|
|
|
# Leave alt screen
|
2019-08-23 21:06:33 +02:00
|
|
|
out.write('\033[m\033[r\033[?1049l')
|
2019-08-21 20:01:17 +02:00
|
|
|
|
2020-11-23 19:30:44 +01:00
|
|
|
with open('/dev/urandom', 'rb') as rand:
|
|
|
|
|
if opts.sixel:
|
|
|
|
|
# The sixel 'alphabet'
|
|
|
|
|
sixels = '?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
|
|
|
|
|
|
|
|
|
|
for _ in range(200):
|
|
|
|
|
# Offset image
|
|
|
|
|
out.write(' ' * (rand.read(1)[0] % (cols // 2)))
|
|
|
|
|
|
|
|
|
|
# Begin sixel
|
|
|
|
|
out.write('\033Pq')
|
|
|
|
|
|
|
|
|
|
# Set up 256 random colors
|
|
|
|
|
for idx in range(256):
|
|
|
|
|
# param 2: 1=HLS, 2=RGB.
|
|
|
|
|
# param 3/4/5: HLS/RGB values in range 0-100
|
|
|
|
|
# (except 'hue' which is 0..360)
|
|
|
|
|
out.write(f'#{idx};2;{rand.read(1)[0] % 101};{rand.read(1)[0] % 101};{rand.read(1)[0] % 101}')
|
|
|
|
|
|
|
|
|
|
# Randomize image width/height
|
|
|
|
|
six_height = struct.unpack('@H', rand.read(2))[0] % (height // 2)
|
|
|
|
|
six_width = struct.unpack('@H', rand.read(2))[0] % (width // 2)
|
|
|
|
|
|
|
|
|
|
# Sixel size. Without this, sixels will be
|
|
|
|
|
# auto-resized on cell-boundaries. We expect programs
|
|
|
|
|
# to emit this sequence since otherwise you cannot get
|
|
|
|
|
# correctly sized images.
|
|
|
|
|
out.write(f'"0;0;{six_width};{six_height}')
|
|
|
|
|
|
|
|
|
|
for row in range(six_height // 6): # Each sixel is 6 pixels
|
|
|
|
|
# Choose a random color
|
|
|
|
|
out.write(f'#{rand.read(1)[0] % 256}')
|
|
|
|
|
|
|
|
|
|
if rand.read(1)[0] == 999999999999:
|
|
|
|
|
assert False
|
|
|
|
|
out.write(f'!{six_width}{sixels[rand.read(1)[0] % len(sixels)]}')
|
|
|
|
|
else:
|
|
|
|
|
for col in range(six_width):
|
|
|
|
|
out.write(f'{sixels[rand.read(1)[0] % len(sixels)]}')
|
|
|
|
|
|
|
|
|
|
# Next line
|
|
|
|
|
out.write('-')
|
|
|
|
|
|
|
|
|
|
# End sixel
|
|
|
|
|
out.write('\033\\')
|
|
|
|
|
|
2019-08-21 20:01:17 +02:00
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
|
sys.exit(main())
|