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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +7 -0
  4. data/README.md +116 -88
  5. data/Rakefile +1 -20
  6. data/bin/reset_cursor +11 -0
  7. data/bin/screen_test +68 -0
  8. data/examples/intro.rb +3 -3
  9. data/examples/scrolling.rb +5 -4
  10. data/lib/whirled_peas.rb +2 -4
  11. data/lib/whirled_peas/animator.rb +5 -0
  12. data/lib/whirled_peas/animator/debug_consumer.rb +17 -0
  13. data/lib/whirled_peas/animator/easing.rb +72 -0
  14. data/lib/whirled_peas/animator/frame.rb +5 -0
  15. data/lib/whirled_peas/animator/frameset.rb +33 -0
  16. data/lib/whirled_peas/animator/producer.rb +35 -0
  17. data/lib/whirled_peas/animator/renderer_consumer.rb +31 -0
  18. data/lib/whirled_peas/command.rb +5 -0
  19. data/lib/whirled_peas/command/base.rb +86 -0
  20. data/lib/whirled_peas/command/config_command.rb +44 -0
  21. data/lib/whirled_peas/command/debug.rb +21 -0
  22. data/lib/whirled_peas/command/fonts.rb +22 -0
  23. data/lib/whirled_peas/command/frame_command.rb +34 -0
  24. data/lib/whirled_peas/command/frames.rb +24 -0
  25. data/lib/whirled_peas/command/help.rb +38 -0
  26. data/lib/whirled_peas/command/play.rb +108 -0
  27. data/lib/whirled_peas/command/record.rb +57 -0
  28. data/lib/whirled_peas/command/still.rb +29 -0
  29. data/lib/whirled_peas/command_line.rb +22 -212
  30. data/lib/whirled_peas/config.rb +56 -6
  31. data/lib/whirled_peas/device.rb +5 -0
  32. data/lib/whirled_peas/device/null_device.rb +8 -0
  33. data/lib/whirled_peas/device/output_file.rb +19 -0
  34. data/lib/whirled_peas/device/screen.rb +26 -0
  35. data/lib/whirled_peas/graphics/container_painter.rb +91 -0
  36. data/lib/whirled_peas/graphics/painter.rb +10 -0
  37. data/lib/whirled_peas/graphics/renderer.rb +8 -2
  38. data/lib/whirled_peas/utils/ansi.rb +13 -0
  39. data/lib/whirled_peas/utils/file_handler.rb +57 -0
  40. data/lib/whirled_peas/version.rb +1 -1
  41. data/tools/whirled_peas/tools/screen_tester.rb +117 -65
  42. metadata +27 -8
  43. data/lib/whirled_peas/frame.rb +0 -6
  44. data/lib/whirled_peas/frame/consumer.rb +0 -30
  45. data/lib/whirled_peas/frame/debug_consumer.rb +0 -30
  46. data/lib/whirled_peas/frame/event_loop.rb +0 -90
  47. data/lib/whirled_peas/frame/producer.rb +0 -67
  48. 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