fusuma 2.5.0 → 3.0.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/README.md +55 -34
- data/bin/console +2 -2
- data/lib/fusuma/config/index.rb +19 -46
- data/lib/fusuma/config/searcher.rb +14 -61
- data/lib/fusuma/custom_process.rb +33 -1
- data/lib/fusuma/device.rb +1 -0
- data/lib/fusuma/multi_logger.rb +16 -0
- data/lib/fusuma/plugin/base.rb +4 -1
- data/lib/fusuma/plugin/buffers/gesture_buffer.rb +63 -6
- data/lib/fusuma/plugin/detectors/detector.rb +10 -9
- data/lib/fusuma/plugin/detectors/hold_detector.rb +24 -14
- data/lib/fusuma/plugin/detectors/pinch_detector.rb +10 -10
- data/lib/fusuma/plugin/detectors/rotate_detector.rb +4 -11
- data/lib/fusuma/plugin/detectors/swipe_detector.rb +7 -15
- data/lib/fusuma/plugin/events/records/gesture_record.rb +5 -2
- data/lib/fusuma/plugin/events/records/index_record.rb +1 -1
- data/lib/fusuma/plugin/executors/command_executor.rb +2 -2
- data/lib/fusuma/plugin/executors/executor.rb +1 -4
- data/lib/fusuma/plugin/filters/libinput_device_filter.rb +2 -1
- data/lib/fusuma/plugin/inputs/input.rb +14 -36
- data/lib/fusuma/plugin/inputs/timer_input.rb +36 -21
- data/lib/fusuma/plugin/manager.rb +48 -22
- data/lib/fusuma/version.rb +1 -1
- data/lib/fusuma.rb +44 -34
- metadata +3 -3
@@ -7,9 +7,25 @@ module Fusuma
|
|
7
7
|
module Buffers
|
8
8
|
# manage events and generate command
|
9
9
|
class GestureBuffer < Buffer
|
10
|
+
CacheEntry = Struct.new(:checked, :value)
|
10
11
|
DEFAULT_SOURCE = "libinput_gesture_parser"
|
11
12
|
DEFAULT_SECONDS_TO_KEEP = 100
|
12
13
|
|
14
|
+
|
15
|
+
def initialize(*args)
|
16
|
+
super(*args)
|
17
|
+
@cache = {}
|
18
|
+
@cache_select_by = {}
|
19
|
+
@cache_sum10 = {}
|
20
|
+
end
|
21
|
+
|
22
|
+
def clear
|
23
|
+
super.clear
|
24
|
+
@cache = {}
|
25
|
+
@cache_select_by = {}
|
26
|
+
@cache_sum10 = {}
|
27
|
+
end
|
28
|
+
|
13
29
|
def config_param_types
|
14
30
|
{
|
15
31
|
source: [String],
|
@@ -40,6 +56,9 @@ module Fusuma
|
|
40
56
|
MultiLogger.debug("#{self.class.name}##{__method__}")
|
41
57
|
|
42
58
|
@events.delete(e)
|
59
|
+
@cache = {}
|
60
|
+
@cache_select_by = {}
|
61
|
+
@cache_sum10 = {}
|
43
62
|
end
|
44
63
|
end
|
45
64
|
|
@@ -59,11 +78,35 @@ module Fusuma
|
|
59
78
|
def sum_attrs(attr)
|
60
79
|
updating_events.map do |gesture_event|
|
61
80
|
gesture_event.record.delta[attr].to_f
|
62
|
-
end.
|
81
|
+
end.reduce(:+)
|
82
|
+
end
|
83
|
+
|
84
|
+
# @param attr [Symbol]
|
85
|
+
# @return [Float]
|
86
|
+
def sum_last10_attrs(attr) # sums last 10 values of attr (or all if length < 10)
|
87
|
+
cache_entry = ( @cache_sum10[attr] ||= CacheEntry.new(0, 0) )
|
88
|
+
upd_ev = updating_events
|
89
|
+
if upd_ev.length > cache_entry.checked + 1 then
|
90
|
+
cache_entry.value = upd_ev.last(10).map do |gesture_event|
|
91
|
+
gesture_event.record.delta[attr].to_f
|
92
|
+
end.reduce(:+)
|
93
|
+
elsif upd_ev.length > cache_entry.checked
|
94
|
+
cache_entry.value = cache_entry.value + upd_ev[-1].record.delta[attr].to_f - \
|
95
|
+
(upd_ev.length > 10 ? upd_ev[-11].record.delta[attr].to_f : 0)
|
96
|
+
else
|
97
|
+
return cache_entry.value
|
98
|
+
end
|
99
|
+
cache_entry.checked = upd_ev.length
|
100
|
+
cache_entry.value
|
63
101
|
end
|
64
102
|
|
65
103
|
def updating_events
|
66
|
-
@
|
104
|
+
cache_entry = ( @cache[:updating_events] ||= CacheEntry.new(0, []) )
|
105
|
+
cache_entry.checked.upto(@events.length - 1).each do |i|
|
106
|
+
(cache_entry.value << @events[i]) if @events[i].record.status == "update"
|
107
|
+
end
|
108
|
+
cache_entry.checked = @events.length
|
109
|
+
cache_entry.value
|
67
110
|
end
|
68
111
|
|
69
112
|
# @param attr [Symbol]
|
@@ -96,14 +139,28 @@ module Fusuma
|
|
96
139
|
self.class.new events
|
97
140
|
end
|
98
141
|
|
142
|
+
def select_by_type(type)
|
143
|
+
cache_entry = ( @cache_select_by[type] ||= CacheEntry.new(0, self.class.new([])) )
|
144
|
+
cache_entry.checked.upto(@events.length - 1).each do |i|
|
145
|
+
(cache_entry.value.events << @events[i]) if @events[i].record.gesture == type
|
146
|
+
end
|
147
|
+
cache_entry.checked = @events.length
|
148
|
+
cache_entry.value
|
149
|
+
end
|
150
|
+
|
99
151
|
def select_from_last_begin
|
100
152
|
return self if empty?
|
153
|
+
cache_entry = ( @cache[:last_begin] ||= CacheEntry.new(0, nil) )
|
154
|
+
|
155
|
+
cache_entry.value = (@events.length - 1).downto(cache_entry.checked).find do |i|
|
156
|
+
@events[i].record.status == "begin"
|
157
|
+
end || cache_entry.value
|
158
|
+
cache_entry.checked = @events.length
|
101
159
|
|
102
|
-
|
103
|
-
return GestureBuffer.new([]) if
|
160
|
+
return self if cache_entry.value == 0
|
161
|
+
return GestureBuffer.new([]) if cache_entry.value.nil?
|
104
162
|
|
105
|
-
|
106
|
-
GestureBuffer.new(@events[index_last_begin..-1])
|
163
|
+
GestureBuffer.new(@events[cache_entry.value..-1])
|
107
164
|
end
|
108
165
|
end
|
109
166
|
end
|
@@ -8,9 +8,18 @@ module Fusuma
|
|
8
8
|
module Detectors
|
9
9
|
# Inherite this base
|
10
10
|
class Detector < Base
|
11
|
+
def initialize(*args)
|
12
|
+
super(*args)
|
13
|
+
@tag = self.class.tag
|
14
|
+
@type = self.class.type
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :tag
|
18
|
+
attr_reader :type
|
19
|
+
|
11
20
|
# @return [Array<String>]
|
12
21
|
def sources
|
13
|
-
@
|
22
|
+
@sources ||= self.class.const_get(:SOURCES)
|
14
23
|
end
|
15
24
|
|
16
25
|
# Always watch buffers and detect them or not
|
@@ -43,14 +52,6 @@ module Fusuma
|
|
43
52
|
@last_time.nil?
|
44
53
|
end
|
45
54
|
|
46
|
-
def tag
|
47
|
-
self.class.tag
|
48
|
-
end
|
49
|
-
|
50
|
-
def type
|
51
|
-
self.class.type
|
52
|
-
end
|
53
|
-
|
54
55
|
class << self
|
55
56
|
def tag
|
56
57
|
name.split("Detectors::").last.underscore
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "./detector"
|
4
|
+
require_relative "../inputs/timer_input"
|
4
5
|
|
5
6
|
module Fusuma
|
6
7
|
module Plugin
|
@@ -10,6 +11,7 @@ module Fusuma
|
|
10
11
|
SOURCES = %w[gesture timer].freeze
|
11
12
|
BUFFER_TYPE = "gesture"
|
12
13
|
GESTURE_RECORD_TYPE = "hold"
|
14
|
+
Timer = Inputs::TimerInput.instance
|
13
15
|
|
14
16
|
BASE_THERESHOLD = 0.7
|
15
17
|
|
@@ -21,16 +23,15 @@ module Fusuma
|
|
21
23
|
hold_buffer = find_hold_buffer(buffers)
|
22
24
|
return if hold_buffer.empty?
|
23
25
|
|
24
|
-
|
26
|
+
last_hold = hold_buffer.events.last
|
25
27
|
|
26
28
|
timer_buffer = buffers.find { |b| b.type == "timer" }
|
27
|
-
|
29
|
+
last_timer = timer_buffer.events.last
|
28
30
|
|
29
31
|
finger = hold_buffer.finger
|
30
|
-
holding_time = calc_holding_time(hold_events:
|
32
|
+
holding_time = calc_holding_time(hold_events: hold_buffer.events, last_timer: last_timer)
|
31
33
|
|
32
|
-
|
33
|
-
status = case hold_events.last.record.status
|
34
|
+
status = case last_hold.record.status
|
34
35
|
when "begin"
|
35
36
|
if holding_time.zero?
|
36
37
|
"begin"
|
@@ -42,16 +43,19 @@ module Fusuma
|
|
42
43
|
when "end"
|
43
44
|
"end"
|
44
45
|
else
|
45
|
-
last_record =
|
46
|
+
last_record = last_hold.record.status
|
46
47
|
raise "Unexpected Status:#{last_record.status} in #{last_record}"
|
47
48
|
end
|
48
49
|
|
49
50
|
repeat_index = create_repeat_index(finger: finger, status: status)
|
50
51
|
oneshot_index = create_oneshot_index(finger: finger)
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
if status == "begin" then
|
54
|
+
@timeout = nil
|
55
|
+
if threshold(index: oneshot_index) < Timer.interval then
|
56
|
+
Timer.wake_early(Time.now + threshold(index: oneshot_index))
|
57
|
+
end
|
58
|
+
elsif status == "timer"
|
55
59
|
return if @timeout
|
56
60
|
|
57
61
|
return unless enough?(index: oneshot_index, holding_time: holding_time)
|
@@ -97,12 +101,12 @@ module Fusuma
|
|
97
101
|
def find_hold_buffer(buffers)
|
98
102
|
buffers.find { |b| b.type == BUFFER_TYPE }
|
99
103
|
.select_from_last_begin
|
100
|
-
.
|
104
|
+
.select_by_type(GESTURE_RECORD_TYPE)
|
101
105
|
end
|
102
106
|
|
103
|
-
def calc_holding_time(hold_events:,
|
104
|
-
last_time = if
|
105
|
-
|
107
|
+
def calc_holding_time(hold_events:, last_timer:)
|
108
|
+
last_time = if last_timer && (hold_events.last.time < last_timer.time)
|
109
|
+
last_timer.time
|
106
110
|
else
|
107
111
|
hold_events.last.time
|
108
112
|
end
|
@@ -110,7 +114,13 @@ module Fusuma
|
|
110
114
|
end
|
111
115
|
|
112
116
|
def enough?(index:, holding_time:)
|
113
|
-
|
117
|
+
diff = threshold(index: index) - holding_time
|
118
|
+
if diff < 0 then
|
119
|
+
true
|
120
|
+
elsif diff < Timer.interval
|
121
|
+
Timer.wake_early(Time.now + diff)
|
122
|
+
false
|
123
|
+
end
|
114
124
|
end
|
115
125
|
|
116
126
|
def threshold(index:)
|
@@ -19,7 +19,7 @@ module Fusuma
|
|
19
19
|
def detect(buffers)
|
20
20
|
gesture_buffer = buffers.find { |b| b.type == BUFFER_TYPE }
|
21
21
|
.select_from_last_begin
|
22
|
-
.
|
22
|
+
.select_by_type(GESTURE_RECORD_TYPE)
|
23
23
|
|
24
24
|
updating_events = gesture_buffer.updating_events
|
25
25
|
return if updating_events.empty?
|
@@ -100,10 +100,10 @@ module Fusuma
|
|
100
100
|
def create_repeat_index(gesture:, finger:, direction:, status:)
|
101
101
|
Config::Index.new(
|
102
102
|
[
|
103
|
-
Config::Index::Key.new(gesture),
|
104
|
-
Config::Index::Key.new(finger.to_i),
|
105
|
-
Config::Index::Key.new(direction, skippable: true),
|
106
|
-
Config::Index::Key.new(status)
|
103
|
+
Config::Index::Key.new(gesture), # 'pinch'
|
104
|
+
Config::Index::Key.new(finger.to_i), # 2, 3, 4
|
105
|
+
Config::Index::Key.new(direction, skippable: true), # 'in', 'out'
|
106
|
+
Config::Index::Key.new(status) # 'begin', 'update', 'end'
|
107
107
|
]
|
108
108
|
)
|
109
109
|
end
|
@@ -115,9 +115,9 @@ module Fusuma
|
|
115
115
|
def create_oneshot_index(gesture:, finger:, direction:)
|
116
116
|
Config::Index.new(
|
117
117
|
[
|
118
|
-
Config::Index::Key.new(gesture),
|
119
|
-
Config::Index::Key.new(finger.to_i, skippable: true),
|
120
|
-
Config::Index::Key.new(direction)
|
118
|
+
Config::Index::Key.new(gesture), # 'pinch'
|
119
|
+
Config::Index::Key.new(finger.to_i, skippable: true), # 2, 3, 4
|
120
|
+
Config::Index::Key.new(direction) # 'in', 'out'
|
121
121
|
]
|
122
122
|
)
|
123
123
|
end
|
@@ -161,9 +161,9 @@ module Fusuma
|
|
161
161
|
|
162
162
|
def calc
|
163
163
|
if @target > @base
|
164
|
-
IN
|
165
|
-
else
|
166
164
|
OUT
|
165
|
+
else
|
166
|
+
IN
|
167
167
|
end
|
168
168
|
end
|
169
169
|
end
|
@@ -19,21 +19,14 @@ module Fusuma
|
|
19
19
|
def detect(buffers)
|
20
20
|
gesture_buffer = buffers.find { |b| b.type == BUFFER_TYPE }
|
21
21
|
.select_from_last_begin
|
22
|
-
.
|
22
|
+
.select_by_type(GESTURE_RECORD_TYPE)
|
23
23
|
|
24
24
|
updating_events = gesture_buffer.updating_events
|
25
25
|
return if updating_events.empty?
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
last_10.sum_attrs(:rotate) / updating_time
|
31
|
-
else
|
32
|
-
updating_time = 100 * (updating_events.last.time - updating_events.first.time)
|
33
|
-
gesture_buffer.sum_attrs(:rotate) / updating_time
|
34
|
-
end
|
35
|
-
|
36
|
-
return if updating_events.empty?
|
27
|
+
updating_time = 100 * (updating_events[-1].time -
|
28
|
+
(updating_events[-10] || updating_events.first).time)
|
29
|
+
oneshot_angle = gesture_buffer.sum_last10_attrs(:rotate) / updating_time
|
37
30
|
|
38
31
|
finger = gesture_buffer.finger
|
39
32
|
|
@@ -19,22 +19,15 @@ module Fusuma
|
|
19
19
|
def detect(buffers)
|
20
20
|
gesture_buffer = buffers.find { |b| b.type == BUFFER_TYPE }
|
21
21
|
.select_from_last_begin
|
22
|
-
.
|
22
|
+
.select_by_type(GESTURE_RECORD_TYPE)
|
23
23
|
|
24
24
|
updating_events = gesture_buffer.updating_events
|
25
25
|
return if updating_events.empty?
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
last_10.sum_attrs(:move_y) / updating_time]
|
32
|
-
else
|
33
|
-
updating_time = 100 * (updating_events.last.time - updating_events.first.time)
|
34
|
-
[gesture_buffer.sum_attrs(:move_x) / updating_time,
|
35
|
-
gesture_buffer.sum_attrs(:move_y) / updating_time]
|
36
|
-
end
|
37
|
-
(gesture_buffer.sum_attrs(:move_x) / updating_time)
|
27
|
+
updating_time = 100 * (updating_events.last.time -
|
28
|
+
(updating_events[-10] || updating_events.first).time)
|
29
|
+
oneshot_move_x = gesture_buffer.sum_last10_attrs(:move_x) / updating_time
|
30
|
+
oneshot_move_y = gesture_buffer.sum_last10_attrs(:move_y) / updating_time
|
38
31
|
|
39
32
|
finger = gesture_buffer.finger
|
40
33
|
status = case gesture_buffer.events.last.record.status
|
@@ -67,8 +60,7 @@ module Fusuma
|
|
67
60
|
|
68
61
|
oneshot_direction = Direction.new(move_x: oneshot_move_x, move_y: oneshot_move_y).to_s
|
69
62
|
oneshot_quantity = Quantity.new(move_x: oneshot_move_x, move_y: oneshot_move_y).to_f
|
70
|
-
oneshot_index = create_oneshot_index(gesture: type, finger: finger,
|
71
|
-
direction: oneshot_direction)
|
63
|
+
oneshot_index = create_oneshot_index(gesture: type, finger: finger, direction: oneshot_direction)
|
72
64
|
if enough_oneshot_threshold?(index: oneshot_index, quantity: oneshot_quantity)
|
73
65
|
return [
|
74
66
|
create_event(record: Events::Records::IndexRecord.new(
|
@@ -109,7 +101,7 @@ module Fusuma
|
|
109
101
|
Config::Index.new(
|
110
102
|
[
|
111
103
|
Config::Index::Key.new(gesture),
|
112
|
-
Config::Index::Key.new(finger.to_i
|
104
|
+
Config::Index::Key.new(finger.to_i),
|
113
105
|
Config::Index::Key.new(direction)
|
114
106
|
]
|
115
107
|
)
|
@@ -11,9 +11,12 @@ module Fusuma
|
|
11
11
|
# define gesture format
|
12
12
|
attr_reader :status, :gesture, :finger, :delta
|
13
13
|
|
14
|
-
Delta = Struct.new(
|
14
|
+
Delta = Struct.new(
|
15
|
+
:move_x, :move_y,
|
15
16
|
:unaccelerated_x, :unaccelerated_y,
|
16
|
-
:zoom,
|
17
|
+
:zoom,
|
18
|
+
:rotate
|
19
|
+
)
|
17
20
|
|
18
21
|
# @param status [String]
|
19
22
|
# @param gesture [String]
|
@@ -39,14 +39,14 @@ module Fusuma
|
|
39
39
|
# @return [String]
|
40
40
|
def search_command(event)
|
41
41
|
command_index = Config::Index.new([*event.record.index.keys, :command])
|
42
|
-
Config.search(command_index)
|
42
|
+
Config.instance.search(command_index)
|
43
43
|
end
|
44
44
|
|
45
45
|
# @param event [Event]
|
46
46
|
# @return [Float]
|
47
47
|
def args_accel(event)
|
48
48
|
accel_index = Config::Index.new([*event.record.index.keys, :accel])
|
49
|
-
(Config.search(accel_index) || 1).to_f
|
49
|
+
(Config.instance.search(accel_index) || 1).to_f
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
@@ -26,12 +26,9 @@ module Fusuma
|
|
26
26
|
end
|
27
27
|
|
28
28
|
# @param event [Events::Event]
|
29
|
-
# @param time [Time]
|
30
29
|
# @return [TrueClass, FalseClass]
|
31
30
|
def enough_interval?(event)
|
32
|
-
|
33
|
-
# Otherwise, a wrong index will cause invalid intervals.
|
34
|
-
return true if event.record.index.with_context.keys.any? { |key| key.symbol == :end }
|
31
|
+
return true if event.record.index.keys.any? { |key| key.symbol == :end }
|
35
32
|
|
36
33
|
return false if @wait_until && event.time < @wait_until
|
37
34
|
|
@@ -25,7 +25,8 @@ module Fusuma
|
|
25
25
|
keep_device.reset
|
26
26
|
return false
|
27
27
|
end
|
28
|
-
|
28
|
+
device_id = record.to_s.match(/\S*/, 1).to_s
|
29
|
+
keep_device.all.map(&:id).include?(device_id)
|
29
30
|
end
|
30
31
|
|
31
32
|
def keep_device
|
@@ -9,8 +9,14 @@ module Fusuma
|
|
9
9
|
# Inherite this base
|
10
10
|
# @abstract Subclass and override {#io} to implement
|
11
11
|
class Input < Base
|
12
|
+
def initialize(*args)
|
13
|
+
super(*args)
|
14
|
+
@tag = self.class.name.split("Inputs::").last.underscore
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :tag
|
18
|
+
|
12
19
|
# Wait multiple inputs until it becomes readable
|
13
|
-
# and read lines with nonblock
|
14
20
|
# @param inputs [Array<Input>]
|
15
21
|
# @return [Event]
|
16
22
|
def self.select(inputs)
|
@@ -20,20 +26,17 @@ module Fusuma
|
|
20
26
|
input = inputs.find { |i| i.io == io }
|
21
27
|
|
22
28
|
begin
|
23
|
-
|
29
|
+
# NOTE: io.readline is blocking method
|
30
|
+
# each input plugin must write line to pipe (include `\n`)
|
31
|
+
line = io.readline(chomp: true)
|
24
32
|
rescue EOFError => e
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
warn "stop process: #{i.class.name.underscore}"
|
29
|
-
Process.kill(:SIGKILL, i.pid)
|
30
|
-
end
|
31
|
-
exit 1
|
33
|
+
MultiLogger.error "#{input.class.name}: #{e}"
|
34
|
+
MultiLogger.error "Shutdown fusuma process..."
|
35
|
+
Process.kill("TERM", Process.pid)
|
32
36
|
rescue => e
|
33
|
-
|
37
|
+
MultiLogger.error "#{input.class.name}: #{e}"
|
34
38
|
exit 1
|
35
39
|
end
|
36
|
-
|
37
40
|
input.create_event(record: line)
|
38
41
|
end
|
39
42
|
|
@@ -53,32 +56,7 @@ module Fusuma
|
|
53
56
|
MultiLogger.debug(input_event: e)
|
54
57
|
e
|
55
58
|
end
|
56
|
-
|
57
|
-
def tag
|
58
|
-
self.class.name.split("Inputs::").last.underscore
|
59
|
-
end
|
60
59
|
end
|
61
60
|
end
|
62
61
|
end
|
63
62
|
end
|
64
|
-
|
65
|
-
# ref: https://github.com/Homebrew/brew/blob/6b2dbbc96f7d8aa12f9b8c9c60107c9cc58befc4/Library/Homebrew/extend/io.rb
|
66
|
-
class IO
|
67
|
-
def readline_nonblock(sep = $INPUT_RECORD_SEPARATOR)
|
68
|
-
line = +""
|
69
|
-
buffer = +""
|
70
|
-
|
71
|
-
loop do
|
72
|
-
break if buffer == sep
|
73
|
-
|
74
|
-
read_nonblock(1, buffer)
|
75
|
-
line.concat(buffer)
|
76
|
-
end
|
77
|
-
|
78
|
-
line.freeze
|
79
|
-
rescue IO::WaitReadable, EOFError => e
|
80
|
-
raise e if line.empty?
|
81
|
-
|
82
|
-
line.freeze
|
83
|
-
end
|
84
|
-
end
|
@@ -1,20 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "./input"
|
4
|
+
require "timeout"
|
4
5
|
|
5
6
|
module Fusuma
|
6
7
|
module Plugin
|
7
8
|
module Inputs
|
8
9
|
# libinput commands wrapper
|
9
10
|
class TimerInput < Input
|
10
|
-
|
11
|
+
include Singleton
|
12
|
+
DEFAULT_INTERVAL = 5
|
13
|
+
EPSILON_TIME = 0.02
|
11
14
|
def config_param_types
|
12
15
|
{
|
13
16
|
interval: [Float]
|
14
17
|
}
|
15
18
|
end
|
16
19
|
|
17
|
-
|
20
|
+
def initialize(*args, interval: nil)
|
21
|
+
super(*args)
|
22
|
+
@interval = interval || config_params(:interval) || DEFAULT_INTERVAL
|
23
|
+
@early_wake_queue = Queue.new
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :pid, :interval
|
18
27
|
|
19
28
|
def io
|
20
29
|
@io ||= begin
|
@@ -26,26 +35,36 @@ module Fusuma
|
|
26
35
|
end
|
27
36
|
|
28
37
|
def start(reader, writer)
|
29
|
-
|
30
|
-
timer_loop(
|
38
|
+
Thread.new do
|
39
|
+
timer_loop(writer)
|
31
40
|
end
|
32
|
-
|
33
|
-
writer.close
|
34
|
-
pid
|
41
|
+
nil
|
35
42
|
end
|
36
43
|
|
37
|
-
def timer_loop(
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
def timer_loop(writer)
|
45
|
+
delta_t = @interval
|
46
|
+
next_wake = Time.now + delta_t
|
47
|
+
loop do
|
48
|
+
sleep_time = next_wake - Time.now
|
49
|
+
if sleep_time <= 0
|
50
|
+
raise Timeout::Error
|
51
|
+
end
|
52
|
+
|
53
|
+
Timeout.timeout(sleep_time) do
|
54
|
+
next_wake = [@early_wake_queue.deq, next_wake].min
|
43
55
|
end
|
44
|
-
rescue
|
45
|
-
|
46
|
-
|
47
|
-
MultiLogger.error e
|
56
|
+
rescue Timeout::Error
|
57
|
+
writer.puts "timer"
|
58
|
+
next_wake = Time.now + delta_t
|
48
59
|
end
|
60
|
+
rescue Errno::EPIPE
|
61
|
+
exit 0
|
62
|
+
rescue => e
|
63
|
+
MultiLogger.error e
|
64
|
+
end
|
65
|
+
|
66
|
+
def wake_early(t)
|
67
|
+
@early_wake_queue.push(t + EPSILON_TIME)
|
49
68
|
end
|
50
69
|
|
51
70
|
private
|
@@ -53,10 +72,6 @@ module Fusuma
|
|
53
72
|
def create_io
|
54
73
|
IO.pipe
|
55
74
|
end
|
56
|
-
|
57
|
-
def interval
|
58
|
-
config_params(:interval) || DEFAULT_INTERVAL
|
59
|
-
end
|
60
75
|
end
|
61
76
|
end
|
62
77
|
end
|