fusuma 2.5.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,21 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fusuma/string_support"
3
+ require_relative "./string_support"
4
4
 
5
5
  module Fusuma
6
6
  # Rename process
7
7
  module CustomProcess
8
8
  attr_writer :proctitle
9
9
 
10
+ def child_pids
11
+ @child_pids ||= []
12
+ end
13
+
10
14
  def fork
11
- Process.fork do
15
+ pid = Process.fork do
12
16
  Process.setproctitle(proctitle)
17
+ set_trap # for child process
13
18
  yield
14
19
  end
20
+ child_pids << pid
21
+ pid
22
+ end
23
+
24
+ def shutdown
25
+ child_pids.each do |pid|
26
+ Process.kill("TERM", pid)
27
+ rescue Errno::ESRCH
28
+ # ignore
29
+ end
30
+
31
+ child_pids.each do |pid|
32
+ Process.wait(pid)
33
+ rescue Errno::ECHILD
34
+ # ignore
35
+ end
15
36
  end
16
37
 
17
38
  def proctitle
18
39
  @proctitle ||= self.class.name.underscore
19
40
  end
41
+
42
+ def set_trap
43
+ Signal.trap("INT") {
44
+ shutdown
45
+ exit
46
+ } # Trap ^C
47
+ Signal.trap("TERM") {
48
+ shutdown
49
+ exit
50
+ } # Trap `Kill `
51
+ end
20
52
  end
21
53
  end
data/lib/fusuma/device.rb CHANGED
@@ -66,6 +66,7 @@ module Fusuma
66
66
  line_parser = LineParser.new
67
67
 
68
68
  libinput_command = Plugin::Inputs::LibinputCommandInput.new.command
69
+ # note: this libinput command takes a nontrivial amout of time (~200ms)
69
70
  libinput_command.list_devices do |line|
70
71
  line_parser.push(line)
71
72
  end
@@ -39,6 +39,12 @@ module Fusuma
39
39
  puts device.name
40
40
  end
41
41
  end
42
+
43
+ def print_config
44
+ Config.instance.keymap.each do |conf|
45
+ puts conf.deep_stringify_keys.to_yaml
46
+ end
47
+ end
42
48
  end
43
49
  end
44
50
  end
@@ -2,6 +2,28 @@
2
2
 
3
3
  # Patch to hash
4
4
  class Hash
5
+ # activesupport-5.2.0/lib/active_support/core_ext/hash/deep_merge.rb
6
+ def deep_merge(other_hash, &block)
7
+ dup.deep_merge!(other_hash, &block)
8
+ end
9
+
10
+ # Same as +deep_merge+, but modifies +self+.
11
+ def deep_merge!(other_hash, &block)
12
+ merge!(other_hash) do |key, this_val, other_val|
13
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
14
+ this_val.deep_merge(other_val, &block)
15
+ elsif block
16
+ block.call(key, this_val, other_val)
17
+ else
18
+ other_val
19
+ end
20
+ end
21
+ end
22
+
23
+ def deep_stringify_keys
24
+ deep_transform_keys(&:to_s)
25
+ end
26
+
5
27
  # activesupport-4.1.1/lib/active_support/core_ext/hash/keys.rb
6
28
  def deep_symbolize_keys
7
29
  deep_transform_keys do |key|
@@ -32,6 +32,8 @@ module Fusuma
32
32
  def debug(msg)
33
33
  return unless debug_mode?
34
34
 
35
+ return if ignore_pattern?(msg)
36
+
35
37
  super(msg)
36
38
  end
37
39
 
@@ -47,6 +49,20 @@ module Fusuma
47
49
  debug_mode
48
50
  end
49
51
 
52
+ def ignore_pattern?(msg)
53
+ # TODO: configurable from config.yml
54
+ pattern = /timer_input/
55
+ case msg
56
+ when Hash
57
+ e = msg.values.find { |v| v.is_a? Fusuma::Plugin::Events::Event }
58
+ return unless e
59
+
60
+ e.tag.match?(pattern)
61
+ else
62
+ false
63
+ end
64
+ end
65
+
50
66
  class << self
51
67
  def info(msg)
52
68
  instance.info(msg)
@@ -8,7 +8,6 @@ module Fusuma
8
8
  module Plugin
9
9
  # Create a Plugin Class with extending this class
10
10
  class Base
11
- include CustomProcess
12
11
  # when inherited from subclass
13
12
  def self.inherited(subclass)
14
13
  super
@@ -22,20 +21,30 @@ module Fusuma
22
21
  Manager.plugins[name]
23
22
  end
24
23
 
24
+ # @abstract override `#shutdown` to implement
25
+ def shutdown
26
+ end
27
+
25
28
  # config parameter name and Type of the value of parameter
26
29
  # @return [Hash]
27
30
  def config_param_types
28
31
  raise NotImplementedError, "override #{self.class.name}##{__method__}"
29
32
  end
30
33
 
34
+ # @param key [Symbol]
35
+ # @param base [Config::Index]
31
36
  # @return [Object]
32
- def config_params(key = nil, base: config_index)
33
- params = Config.search(base) || {}
37
+ def config_params(key = nil)
38
+ @config_params ||= {}
39
+ if @config_params["#{config_index.cache_key},#{key}"]
40
+ return @config_params["#{config_index.cache_key},#{key}"]
41
+ end
42
+
43
+ params = Config.instance.fetch_config_params(key, config_index)
34
44
 
35
45
  return params unless key
36
46
 
37
- @config_params ||= {}
38
- @config_params["#{base.cache_key},#{key}"] ||=
47
+ @config_params["#{config_index.cache_key},#{key}"] =
39
48
  params.fetch(key, nil).tap do |val|
40
49
  next if val.nil?
41
50
 
@@ -45,14 +54,14 @@ module Fusuma
45
54
  next if param_types.any? { |klass| val.is_a?(klass) }
46
55
 
47
56
  MultiLogger.error("Please fix config.yml.")
48
- MultiLogger.error(":#{base.keys.map(&:symbol)
57
+ MultiLogger.error(":#{config_index.keys.map(&:symbol)
49
58
  .join(" => :")} => :#{key} should be #{param_types.join(" OR ")}.")
50
59
  exit 1
51
60
  end
52
61
  end
53
62
 
54
63
  def config_index
55
- Config::Index.new(self.class.name.gsub("Fusuma::", "").underscore.split("/"))
64
+ @config_index ||= Config::Index.new(self.class.name.gsub("Fusuma::", "").underscore.split("/"))
56
65
  end
57
66
  end
58
67
  end
@@ -7,9 +7,24 @@ 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
+ def initialize(*args)
15
+ super(*args)
16
+ @cache = {}
17
+ @cache_select_by = {}
18
+ @cache_sum10 = {}
19
+ end
20
+
21
+ def clear
22
+ super.clear
23
+ @cache = {}
24
+ @cache_select_by = {}
25
+ @cache_sum10 = {}
26
+ end
27
+
13
28
  def config_param_types
14
29
  {
15
30
  source: [String],
@@ -40,6 +55,9 @@ module Fusuma
40
55
  MultiLogger.debug("#{self.class.name}##{__method__}")
41
56
 
42
57
  @events.delete(e)
58
+ @cache = {}
59
+ @cache_select_by = {}
60
+ @cache_sum10 = {}
43
61
  end
44
62
  end
45
63
 
@@ -59,11 +77,35 @@ module Fusuma
59
77
  def sum_attrs(attr)
60
78
  updating_events.map do |gesture_event|
61
79
  gesture_event.record.delta[attr].to_f
62
- end.inject(:+)
80
+ end.reduce(:+)
81
+ end
82
+
83
+ # @param attr [Symbol]
84
+ # @return [Float]
85
+ def sum_last10_attrs(attr) # sums last 10 values of attr (or all if length < 10)
86
+ cache_entry = (@cache_sum10[attr] ||= CacheEntry.new(0, 0))
87
+ upd_ev = updating_events
88
+ if upd_ev.length > cache_entry.checked + 1
89
+ cache_entry.value = upd_ev.last(10).map do |gesture_event|
90
+ gesture_event.record.delta[attr].to_f
91
+ end.reduce(:+)
92
+ elsif upd_ev.length > cache_entry.checked
93
+ cache_entry.value = cache_entry.value + upd_ev[-1].record.delta[attr].to_f - \
94
+ ((upd_ev.length > 10) ? upd_ev[-11].record.delta[attr].to_f : 0)
95
+ else
96
+ return cache_entry.value
97
+ end
98
+ cache_entry.checked = upd_ev.length
99
+ cache_entry.value
63
100
  end
64
101
 
65
102
  def updating_events
66
- @events.select { |e| e.record.status == "update" }
103
+ cache_entry = (@cache[:updating_events] ||= CacheEntry.new(0, []))
104
+ cache_entry.checked.upto(@events.length - 1).each do |i|
105
+ (cache_entry.value << @events[i]) if @events[i].record.status == "update"
106
+ end
107
+ cache_entry.checked = @events.length
108
+ cache_entry.value
67
109
  end
68
110
 
69
111
  # @param attr [Symbol]
@@ -96,14 +138,28 @@ module Fusuma
96
138
  self.class.new events
97
139
  end
98
140
 
141
+ def select_by_type(type)
142
+ cache_entry = (@cache_select_by[type] ||= CacheEntry.new(0, self.class.new([])))
143
+ cache_entry.checked.upto(@events.length - 1).each do |i|
144
+ (cache_entry.value.events << @events[i]) if @events[i].record.gesture == type
145
+ end
146
+ cache_entry.checked = @events.length
147
+ cache_entry.value
148
+ end
149
+
99
150
  def select_from_last_begin
100
151
  return self if empty?
152
+ cache_entry = (@cache[:last_begin] ||= CacheEntry.new(0, nil))
153
+
154
+ cache_entry.value = (@events.length - 1).downto(cache_entry.checked).find do |i|
155
+ @events[i].record.status == "begin"
156
+ end || cache_entry.value
157
+ cache_entry.checked = @events.length
101
158
 
102
- index_from_last = @events.reverse.find_index { |e| e.record.status == "begin" }
103
- return GestureBuffer.new([]) if index_from_last.nil?
159
+ return self if cache_entry.value == 0
160
+ return GestureBuffer.new([]) if cache_entry.value.nil?
104
161
 
105
- index_last_begin = events.length - index_from_last - 1
106
- GestureBuffer.new(@events[index_last_begin..-1])
162
+ GestureBuffer.new(@events[cache_entry.value..-1])
107
163
  end
108
164
  end
109
165
  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
- @source ||= self.class.const_get(:SOURCES)
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
@@ -13,6 +14,11 @@ module Fusuma
13
14
 
14
15
  BASE_THERESHOLD = 0.7
15
16
 
17
+ def initialize(*args)
18
+ super(*args)
19
+ @timer = Inputs::TimerInput.instance
20
+ end
21
+
16
22
  # @param buffers [Array<Buffers::Buffer>]
17
23
  # @return [Events::Event] if event is detected
18
24
  # @return [Array<Events::Event>] if hold end event is detected
@@ -21,16 +27,15 @@ module Fusuma
21
27
  hold_buffer = find_hold_buffer(buffers)
22
28
  return if hold_buffer.empty?
23
29
 
24
- hold_events = hold_buffer.events
30
+ last_hold = hold_buffer.events.last
25
31
 
26
32
  timer_buffer = buffers.find { |b| b.type == "timer" }
27
- timer_events = timer_buffer.events
33
+ last_timer = timer_buffer.events.last
28
34
 
29
35
  finger = hold_buffer.finger
30
- holding_time = calc_holding_time(hold_events: hold_events, timer_events: timer_events)
36
+ holding_time = calc_holding_time(hold_events: hold_buffer.events, last_timer: last_timer)
31
37
 
32
- @timeout ||= nil
33
- status = case hold_events.last.record.status
38
+ status = case last_hold.record.status
34
39
  when "begin"
35
40
  if holding_time.zero?
36
41
  "begin"
@@ -42,16 +47,19 @@ module Fusuma
42
47
  when "end"
43
48
  "end"
44
49
  else
45
- last_record = hold_events.last.record.status
50
+ last_record = last_hold.record.status
46
51
  raise "Unexpected Status:#{last_record.status} in #{last_record}"
47
52
  end
48
53
 
49
54
  repeat_index = create_repeat_index(finger: finger, status: status)
50
55
  oneshot_index = create_oneshot_index(finger: finger)
51
56
 
52
- @timeout = nil if status == "begin"
53
-
54
- if status == "timer"
57
+ if status == "begin"
58
+ @timeout = nil
59
+ if threshold(index: oneshot_index) < @timer.interval
60
+ @timer.wake_early(Time.now + threshold(index: oneshot_index))
61
+ end
62
+ elsif status == "timer"
55
63
  return if @timeout
56
64
 
57
65
  return unless enough?(index: oneshot_index, holding_time: holding_time)
@@ -97,12 +105,12 @@ module Fusuma
97
105
  def find_hold_buffer(buffers)
98
106
  buffers.find { |b| b.type == BUFFER_TYPE }
99
107
  .select_from_last_begin
100
- .select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
108
+ .select_by_type(GESTURE_RECORD_TYPE)
101
109
  end
102
110
 
103
- def calc_holding_time(hold_events:, timer_events:)
104
- last_time = if !timer_events.empty? && (hold_events.last.time < timer_events.last.time)
105
- timer_events.last.time
111
+ def calc_holding_time(hold_events:, last_timer:)
112
+ last_time = if last_timer && (hold_events.last.time < last_timer.time)
113
+ last_timer.time
106
114
  else
107
115
  hold_events.last.time
108
116
  end
@@ -110,7 +118,13 @@ module Fusuma
110
118
  end
111
119
 
112
120
  def enough?(index:, holding_time:)
113
- holding_time > threshold(index: index)
121
+ diff = threshold(index: index) - holding_time
122
+ if diff < 0
123
+ true
124
+ elsif diff < @timer.interval
125
+ @timer.wake_early(Time.now + diff)
126
+ false
127
+ end
114
128
  end
115
129
 
116
130
  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
- .select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
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
- .select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
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
- oneshot_angle = if updating_events.size >= 10
28
- updating_time = 100 * (updating_events[-1].time - updating_events[-10].time)
29
- last_10 = gesture_buffer.class.new(updating_events[-10..-1])
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
- .select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
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
- oneshot_move_x, oneshot_move_y = if updating_events.size >= 10
28
- updating_time = 100 * (updating_events[-1].time - updating_events[-10].time)
29
- last_10 = gesture_buffer.class.new(updating_events[-10..-1])
30
- [last_10.sum_attrs(:move_x) / updating_time,
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, skippable: true),
104
+ Config::Index::Key.new(finger.to_i),
113
105
  Config::Index::Key.new(direction)
114
106
  ]
115
107
  )
@@ -175,7 +167,7 @@ module Fusuma
175
167
  end
176
168
 
177
169
  def calc
178
- @x > @y ? @x.abs : @y.abs
170
+ (@x > @y) ? @x.abs : @y.abs
179
171
  end
180
172
  end
181
173
  end
@@ -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(:move_x, :move_y,
14
+ Delta = Struct.new(
15
+ :move_x, :move_y,
15
16
  :unaccelerated_x, :unaccelerated_y,
16
- :zoom, :rotate)
17
+ :zoom,
18
+ :rotate
19
+ )
17
20
 
18
21
  # @param status [String]
19
22
  # @param gesture [String]
@@ -35,7 +35,7 @@ module Fusuma
35
35
  raise "position is NOT body: #{self}" unless mergable?
36
36
 
37
37
  if records.empty?
38
- if Config.find_execute_key(index)
38
+ if Config.instance.find_execute_key(index)
39
39
  @index = index
40
40
  return self
41
41
  end
@@ -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
- # NOTE: Cache at the index that is actually used, reflecting Fallback and Skip.
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
- keep_device.all.map(&:id).any? { |device_id| record.to_s =~ /^[\s-]?#{device_id}\s/ }
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