whirled_peas 0.7.1 → 0.8.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +7 -0
- data/README.md +116 -88
- data/Rakefile +1 -20
- data/bin/reset_cursor +11 -0
- data/bin/screen_test +68 -0
- data/examples/intro.rb +3 -3
- data/examples/scrolling.rb +5 -4
- data/lib/whirled_peas.rb +2 -4
- data/lib/whirled_peas/animator.rb +5 -0
- data/lib/whirled_peas/animator/debug_consumer.rb +17 -0
- data/lib/whirled_peas/animator/easing.rb +72 -0
- data/lib/whirled_peas/animator/frame.rb +5 -0
- data/lib/whirled_peas/animator/frameset.rb +33 -0
- data/lib/whirled_peas/animator/producer.rb +35 -0
- data/lib/whirled_peas/animator/renderer_consumer.rb +31 -0
- data/lib/whirled_peas/command.rb +5 -0
- data/lib/whirled_peas/command/base.rb +86 -0
- data/lib/whirled_peas/command/config_command.rb +44 -0
- data/lib/whirled_peas/command/debug.rb +21 -0
- data/lib/whirled_peas/command/fonts.rb +22 -0
- data/lib/whirled_peas/command/frame_command.rb +34 -0
- data/lib/whirled_peas/command/frames.rb +24 -0
- data/lib/whirled_peas/command/help.rb +38 -0
- data/lib/whirled_peas/command/play.rb +108 -0
- data/lib/whirled_peas/command/record.rb +57 -0
- data/lib/whirled_peas/command/still.rb +29 -0
- data/lib/whirled_peas/command_line.rb +22 -212
- data/lib/whirled_peas/config.rb +56 -6
- data/lib/whirled_peas/device.rb +5 -0
- data/lib/whirled_peas/device/null_device.rb +8 -0
- data/lib/whirled_peas/device/output_file.rb +19 -0
- data/lib/whirled_peas/device/screen.rb +26 -0
- data/lib/whirled_peas/graphics/container_painter.rb +91 -0
- data/lib/whirled_peas/graphics/painter.rb +10 -0
- data/lib/whirled_peas/graphics/renderer.rb +8 -2
- data/lib/whirled_peas/utils/ansi.rb +13 -0
- data/lib/whirled_peas/utils/file_handler.rb +57 -0
- data/lib/whirled_peas/version.rb +1 -1
- data/tools/whirled_peas/tools/screen_tester.rb +117 -65
- metadata +27 -8
- data/lib/whirled_peas/frame.rb +0 -6
- data/lib/whirled_peas/frame/consumer.rb +0 -30
- data/lib/whirled_peas/frame/debug_consumer.rb +0 -30
- data/lib/whirled_peas/frame/event_loop.rb +0 -90
- data/lib/whirled_peas/frame/producer.rb +0 -67
- data/lib/whirled_peas/graphics/screen.rb +0 -70
@@ -1,90 +0,0 @@
|
|
1
|
-
require 'whirled_peas/null_logger'
|
2
|
-
require 'whirled_peas/graphics/screen'
|
3
|
-
|
4
|
-
require_relative 'consumer'
|
5
|
-
|
6
|
-
module WhirledPeas
|
7
|
-
module Frame
|
8
|
-
class EventLoop < Consumer
|
9
|
-
DEFAULT_REFRESH_RATE = 30
|
10
|
-
|
11
|
-
LOGGER_ID = 'EVENT LOOP'
|
12
|
-
|
13
|
-
def initialize(
|
14
|
-
template_factory,
|
15
|
-
loading_template_factory=nil,
|
16
|
-
refresh_rate: DEFAULT_REFRESH_RATE,
|
17
|
-
logger: NullLogger.new,
|
18
|
-
screen: Graphics::Screen.new
|
19
|
-
)
|
20
|
-
@template_factory = template_factory
|
21
|
-
@loading_template_factory = loading_template_factory
|
22
|
-
@queue = Queue.new
|
23
|
-
@frame_duration = 1.0 / refresh_rate
|
24
|
-
@logger = logger
|
25
|
-
@screen = screen
|
26
|
-
end
|
27
|
-
|
28
|
-
def enqueue(name, duration, args)
|
29
|
-
# If duration is nil, set it to the duration of a single frame
|
30
|
-
queue.push([name, duration || frame_duration, args])
|
31
|
-
end
|
32
|
-
|
33
|
-
def start
|
34
|
-
super
|
35
|
-
wait_for_content
|
36
|
-
play_content
|
37
|
-
rescue
|
38
|
-
self.running = false
|
39
|
-
logger.warn(LOGGER_ID) { 'Exiting with error' }
|
40
|
-
raise
|
41
|
-
ensure
|
42
|
-
screen.finalize
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
attr_reader :template_factory, :loading_template_factory, :queue, :frame_duration, :logger, :screen
|
48
|
-
|
49
|
-
def wait_for_content
|
50
|
-
if loading_template_factory
|
51
|
-
play_loading_screen
|
52
|
-
else
|
53
|
-
sleep(frame_duration) while queue.empty?
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def play_loading_screen
|
58
|
-
while queue.empty?
|
59
|
-
screen.paint(loading_template_factory.build)
|
60
|
-
sleep(frame_duration)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def play_content
|
65
|
-
template = nil
|
66
|
-
frame_until = Time.new(0) # Tell the loop to immediately pick up a new frame
|
67
|
-
while running?
|
68
|
-
frame_start = Time.now
|
69
|
-
next_frame_at = frame_start + frame_duration
|
70
|
-
if frame_until > frame_start
|
71
|
-
# While we're still displaying the previous frame, refresh the screen
|
72
|
-
screen.refresh
|
73
|
-
elsif !queue.empty?
|
74
|
-
name, duration, args = queue.pop
|
75
|
-
if name == EOF
|
76
|
-
self.running = false
|
77
|
-
else
|
78
|
-
frame_until = frame_start + duration
|
79
|
-
template = template_factory.build(name, args)
|
80
|
-
screen.paint(template)
|
81
|
-
end
|
82
|
-
else
|
83
|
-
wait_for_content
|
84
|
-
end
|
85
|
-
sleep([0, next_frame_at - Time.now].max)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
require 'socket'
|
2
|
-
require 'json'
|
3
|
-
|
4
|
-
require 'whirled_peas/null_logger'
|
5
|
-
|
6
|
-
module WhirledPeas
|
7
|
-
module Frame
|
8
|
-
# A Producer is the object given to the driver as the interface that allows
|
9
|
-
# the driver to emit frame events. The recommended way of creating a Producer
|
10
|
-
# is by invoking `Producer.produce` as it handles the lifecycle methods of
|
11
|
-
# the consumer.
|
12
|
-
class Producer
|
13
|
-
LOGGER_ID = 'PRODUCER'
|
14
|
-
|
15
|
-
# Manages the consumer lifecycle and yields a Producer to send frames to the
|
16
|
-
# consumer
|
17
|
-
#
|
18
|
-
# @param consumer [Consumer] instance that consumes frame events through
|
19
|
-
# `#enqueue`
|
20
|
-
def self.produce(consumer, logger=NullLogger.new)
|
21
|
-
producer = new(consumer, logger)
|
22
|
-
consumer_thread = Thread.new do
|
23
|
-
Thread.current.report_on_exception = false
|
24
|
-
consumer.start
|
25
|
-
end
|
26
|
-
yield producer
|
27
|
-
producer.flush
|
28
|
-
rescue => e
|
29
|
-
logger.warn(LOGGER_ID) { 'Exited with error' }
|
30
|
-
logger.error(LOGGER_ID) { e }
|
31
|
-
raise
|
32
|
-
ensure
|
33
|
-
consumer.stop
|
34
|
-
consumer_thread.join if consumer_thread
|
35
|
-
end
|
36
|
-
|
37
|
-
def initialize(consumer, logger=NullLogger.new)
|
38
|
-
@consumer = consumer
|
39
|
-
@logger = logger
|
40
|
-
@queue = Queue.new
|
41
|
-
end
|
42
|
-
|
43
|
-
# Buffer a frame to be played for the given duration. `#flush` must be called
|
44
|
-
# for frames to get pushed to the EventLoop.
|
45
|
-
#
|
46
|
-
# @param name [String] name of frame, which is passed to #build of the
|
47
|
-
# TemplateFactory
|
48
|
-
# @param duration [Float|Integer] duration in seconds the frame should be,
|
49
|
-
# displayed (default is nil, which results in a duration of a single refresh
|
50
|
-
# cycle)
|
51
|
-
# @param args [Hash] key/value pair of arguments, which is passed to #build of
|
52
|
-
# the TemplateFactory
|
53
|
-
def send_frame(name, duration: nil, args: {})
|
54
|
-
queue.push([name, duration, args])
|
55
|
-
end
|
56
|
-
|
57
|
-
# Send any buffered frames to the EventLoop
|
58
|
-
def flush
|
59
|
-
consumer.enqueue(*queue.pop) while !queue.empty?
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
|
64
|
-
attr_reader :consumer, :logger, :queue
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
@@ -1,70 +0,0 @@
|
|
1
|
-
require 'highline'
|
2
|
-
|
3
|
-
require 'whirled_peas/utils/ansi'
|
4
|
-
|
5
|
-
require_relative 'renderer'
|
6
|
-
|
7
|
-
module WhirledPeas
|
8
|
-
module Graphics
|
9
|
-
class Screen
|
10
|
-
def self.current_dimensions
|
11
|
-
width, height = HighLine.new.terminal.terminal_size
|
12
|
-
[width || 0, height || 0]
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize(width: nil, height: nil, output: STDOUT)
|
16
|
-
@output = output
|
17
|
-
@terminal = HighLine.new.terminal
|
18
|
-
@strokes = []
|
19
|
-
if width && height
|
20
|
-
@width = width
|
21
|
-
@height = height
|
22
|
-
else
|
23
|
-
refresh_size!
|
24
|
-
Signal.trap('SIGWINCH', proc { self.refresh_size! })
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def paint(template)
|
29
|
-
@template = template
|
30
|
-
draw
|
31
|
-
end
|
32
|
-
|
33
|
-
def refresh
|
34
|
-
# No need to refresh if the screen dimensions have not changed
|
35
|
-
return if @refreshed_width == width || @refreshed_height == height
|
36
|
-
draw
|
37
|
-
end
|
38
|
-
|
39
|
-
def finalize
|
40
|
-
output.print Utils::Ansi.clear
|
41
|
-
output.print Utils::Ansi.cursor_pos(top: height - 1)
|
42
|
-
output.print Utils::Ansi.cursor_visible(true)
|
43
|
-
output.flush
|
44
|
-
end
|
45
|
-
|
46
|
-
protected
|
47
|
-
|
48
|
-
def refresh_size!
|
49
|
-
@width, @height = self.class.current_dimensions
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
attr_reader :output, :cursor, :terminal, :width, :height
|
55
|
-
|
56
|
-
def draw
|
57
|
-
strokes = [Utils::Ansi.cursor_visible(false), Utils::Ansi.cursor_pos, Utils::Ansi.clear_down]
|
58
|
-
Renderer.new(@template, width, height).paint do |left, top, fstring|
|
59
|
-
next unless fstring.length > 0
|
60
|
-
strokes << Utils::Ansi.cursor_pos(left: left, top: top)
|
61
|
-
strokes << fstring
|
62
|
-
end
|
63
|
-
strokes.each { |stroke| output.print(stroke) }
|
64
|
-
output.flush
|
65
|
-
@refreshed_width = width
|
66
|
-
@refreshed_height = height
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|