fusuma 1.11.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +49 -7
  3. data/fusuma.gemspec +6 -16
  4. data/lib/fusuma.rb +91 -28
  5. data/lib/fusuma/config.rb +34 -62
  6. data/lib/fusuma/config/index.rb +39 -6
  7. data/lib/fusuma/config/searcher.rb +166 -0
  8. data/lib/fusuma/custom_process.rb +13 -0
  9. data/lib/fusuma/device.rb +22 -7
  10. data/lib/fusuma/environment.rb +6 -4
  11. data/lib/fusuma/hash_support.rb +40 -0
  12. data/lib/fusuma/libinput_command.rb +17 -21
  13. data/lib/fusuma/multi_logger.rb +2 -6
  14. data/lib/fusuma/plugin/base.rb +18 -15
  15. data/lib/fusuma/plugin/buffers/buffer.rb +3 -2
  16. data/lib/fusuma/plugin/buffers/gesture_buffer.rb +34 -25
  17. data/lib/fusuma/plugin/buffers/timer_buffer.rb +46 -0
  18. data/lib/fusuma/plugin/detectors/detector.rb +26 -5
  19. data/lib/fusuma/plugin/detectors/pinch_detector.rb +109 -58
  20. data/lib/fusuma/plugin/detectors/rotate_detector.rb +91 -50
  21. data/lib/fusuma/plugin/detectors/swipe_detector.rb +93 -56
  22. data/lib/fusuma/plugin/events/event.rb +5 -4
  23. data/lib/fusuma/plugin/events/records/context_record.rb +27 -0
  24. data/lib/fusuma/plugin/events/records/gesture_record.rb +9 -6
  25. data/lib/fusuma/plugin/events/records/index_record.rb +46 -14
  26. data/lib/fusuma/plugin/events/records/record.rb +1 -1
  27. data/lib/fusuma/plugin/events/records/text_record.rb +2 -1
  28. data/lib/fusuma/plugin/executors/command_executor.rb +21 -6
  29. data/lib/fusuma/plugin/executors/executor.rb +45 -3
  30. data/lib/fusuma/plugin/filters/filter.rb +1 -1
  31. data/lib/fusuma/plugin/filters/libinput_device_filter.rb +6 -7
  32. data/lib/fusuma/plugin/filters/libinput_timeout_filter.rb +2 -2
  33. data/lib/fusuma/plugin/inputs/input.rb +64 -8
  34. data/lib/fusuma/plugin/inputs/libinput_command_input.rb +19 -9
  35. data/lib/fusuma/plugin/inputs/timer_input.rb +63 -0
  36. data/lib/fusuma/plugin/manager.rb +22 -29
  37. data/lib/fusuma/plugin/parsers/libinput_gesture_parser.rb +10 -8
  38. data/lib/fusuma/plugin/parsers/parser.rb +8 -9
  39. data/lib/fusuma/string_support.rb +16 -0
  40. data/lib/fusuma/version.rb +1 -1
  41. data/spec/helpers/config_helper.rb +20 -0
  42. data/spec/lib/config/searcher_spec.rb +97 -0
  43. data/spec/lib/config_spec.rb +112 -0
  44. data/spec/lib/custom_process_spec.rb +28 -0
  45. data/spec/lib/device_spec.rb +98 -0
  46. data/spec/lib/dummy_config.yml +31 -0
  47. data/spec/lib/fusuma_spec.rb +103 -0
  48. data/spec/lib/libinput-list-devices_iberianpig-XPS-9360.txt +181 -0
  49. data/spec/lib/libinput-list-devices_magic_trackpad.txt +51 -0
  50. data/spec/lib/libinput-list-devices_razer_razer_blade.txt +252 -0
  51. data/spec/lib/libinput-list-devices_thejinx0r.txt +361 -0
  52. data/spec/lib/libinput-list-devices_unavailable.txt +36 -0
  53. data/spec/lib/libinput_command_spec.rb +167 -0
  54. data/spec/lib/plugin/base_spec.rb +74 -0
  55. data/spec/lib/plugin/buffers/buffer_spec.rb +80 -0
  56. data/spec/lib/plugin/buffers/dummy_buffer.rb +20 -0
  57. data/spec/lib/plugin/buffers/gesture_buffer_spec.rb +172 -0
  58. data/spec/lib/plugin/detectors/detector_spec.rb +43 -0
  59. data/spec/lib/plugin/detectors/dummy_detector.rb +24 -0
  60. data/spec/lib/plugin/detectors/pinch_detector_spec.rb +119 -0
  61. data/spec/lib/plugin/detectors/rotate_detector_spec.rb +125 -0
  62. data/spec/lib/plugin/detectors/swipe_detector_spec.rb +118 -0
  63. data/spec/lib/plugin/events/event_spec.rb +30 -0
  64. data/spec/lib/plugin/events/records/gesture_record_spec.rb +22 -0
  65. data/spec/lib/plugin/events/records/record_spec.rb +31 -0
  66. data/spec/lib/plugin/events/records/text_record_spec.rb +26 -0
  67. data/spec/lib/plugin/executors/command_executor_spec.rb +57 -0
  68. data/spec/lib/plugin/executors/executor_spec.rb +160 -0
  69. data/spec/lib/plugin/filters/filter_spec.rb +92 -0
  70. data/spec/lib/plugin/filters/libinput_filter_spec.rb +120 -0
  71. data/spec/lib/plugin/inputs/input_spec.rb +70 -0
  72. data/spec/lib/plugin/inputs/libinput_command_input_spec.rb +120 -0
  73. data/spec/lib/plugin/inputs/timer_input_spec.rb +40 -0
  74. data/spec/lib/plugin/manager_spec.rb +27 -0
  75. data/spec/lib/plugin/parsers/parser_spec.rb +45 -0
  76. data/spec/spec_helper.rb +20 -0
  77. metadata +90 -167
  78. data/.github/FUNDING.yml +0 -8
  79. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -32
  80. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -17
  81. data/.github/pull_request_template.md +0 -9
  82. data/.github/stale.yml +0 -18
  83. data/.gitignore +0 -17
  84. data/.reek.yml +0 -96
  85. data/.rspec +0 -2
  86. data/.rubocop.yml +0 -37
  87. data/.rubocop_todo.yml +0 -40
  88. data/.travis.yml +0 -11
  89. data/CHANGELOG.md +0 -456
  90. data/CODE_OF_CONDUCT.md +0 -74
  91. data/CONTRIBUTING.md +0 -72
  92. data/Gemfile +0 -6
  93. data/Rakefile +0 -15
@@ -1,47 +1,106 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './detector.rb'
3
+ require_relative './detector'
4
4
 
5
5
  module Fusuma
6
6
  module Plugin
7
7
  module Detectors
8
8
  class RotateDetector < Detector
9
+ SOURCES = ['gesture'].freeze
9
10
  BUFFER_TYPE = 'gesture'
10
11
  GESTURE_RECORD_TYPE = 'pinch'
11
12
 
12
13
  FINGERS = [2, 3, 4].freeze
13
14
  BASE_THERESHOLD = 0.5
14
- BASE_INTERVAL = 0.1
15
15
 
16
16
  # @param buffers [Array<Buffer>]
17
- # @return [Event] if event is detected
17
+ # @return [Events::Event] if event is detected
18
18
  # @return [NilClass] if event is NOT detected
19
19
  def detect(buffers)
20
- buffer = buffers.find { |b| b.type == BUFFER_TYPE }
21
- .select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
22
-
23
- return if buffer.empty?
24
-
25
- angle = buffer.avg_attrs(:rotate)
26
-
27
- finger = buffer.finger
28
- direction = Direction.new(angle: angle).to_s
29
- quantity = Quantity.new(angle: angle).to_f
30
-
31
- index = create_index(gesture: type,
32
- finger: finger,
33
- direction: direction)
34
-
35
- return unless enough?(index: index, quantity: quantity)
20
+ gesture_buffer = buffers.find { |b| b.type == BUFFER_TYPE }
21
+ .select_from_last_begin
22
+ .select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
23
+
24
+ updating_events = gesture_buffer.updating_events
25
+ return if updating_events.empty?
26
+
27
+ updating_time = 100 * (updating_events.last.time - updating_events.first.time)
28
+ oneshot_angle = gesture_buffer.sum_attrs(:rotate) / updating_time
29
+
30
+ return if updating_events.empty?
31
+
32
+ finger = gesture_buffer.finger
33
+
34
+ status = case gesture_buffer.events.last.record.status
35
+ when 'end'
36
+ 'end'
37
+ when 'update'
38
+ if updating_events.length == 1
39
+ 'begin'
40
+ else
41
+ 'update'
42
+ end
43
+ else
44
+ gesture_buffer.events.last.record.status
45
+ end
46
+
47
+ delta = if status == 'end'
48
+ gesture_buffer.events[-2].record.delta
49
+ else
50
+ gesture_buffer.events.last.record.delta
51
+ end
52
+
53
+ repeat_direction = Direction.new(angle: delta.rotate).to_s
54
+ repeat_quantity = Quantity.new(angle: delta.rotate).to_f
55
+
56
+ repeat_index = create_repeat_index(gesture: type, finger: finger,
57
+ direction: repeat_direction,
58
+ status: status)
59
+
60
+ if status == 'update'
61
+ return unless moved?(repeat_quantity)
62
+
63
+ oneshot_direction = Direction.new(angle: oneshot_angle).to_s
64
+ oneshot_quantity = Quantity.new(angle: oneshot_angle).to_f
65
+ oneshot_index = create_oneshot_index(gesture: type, finger: finger,
66
+ direction: oneshot_direction)
67
+ if enough_oneshot_threshold?(index: oneshot_index, quantity: oneshot_quantity)
68
+ return [
69
+ create_event(record: Events::Records::IndexRecord.new(
70
+ index: oneshot_index, trigger: :oneshot, args: delta.to_h
71
+ )),
72
+ create_event(record: Events::Records::IndexRecord.new(
73
+ index: repeat_index, trigger: :repeat, args: delta.to_h
74
+ ))
75
+ ]
76
+ end
77
+ end
78
+ create_event(record: Events::Records::IndexRecord.new(
79
+ index: repeat_index, trigger: :repeat, args: delta.to_h
80
+ ))
81
+ end
36
82
 
37
- create_event(record: Events::Records::IndexRecord.new(index: index))
83
+ # @param [String] gesture
84
+ # @param [Integer] finger
85
+ # @param [String] direction
86
+ # @param [String] status
87
+ # @return [Config::Index]
88
+ def create_repeat_index(gesture:, finger:, direction:, status:)
89
+ Config::Index.new(
90
+ [
91
+ Config::Index::Key.new(gesture),
92
+ Config::Index::Key.new(finger.to_i),
93
+ Config::Index::Key.new(direction),
94
+ Config::Index::Key.new(status)
95
+ ]
96
+ )
38
97
  end
39
98
 
40
99
  # @param [String] gesture
41
100
  # @param [Integer] finger
42
101
  # @param [String] direction
43
102
  # @return [Config::Index]
44
- def create_index(gesture:, finger:, direction:)
103
+ def create_oneshot_index(gesture:, finger:, direction:)
45
104
  Config::Index.new(
46
105
  [
47
106
  Config::Index::Key.new(gesture),
@@ -53,41 +112,23 @@ module Fusuma
53
112
 
54
113
  private
55
114
 
56
- def enough?(index:, quantity:)
57
- enough_interval?(index: index) && enough_angle?(index: index, quantity: quantity)
115
+ def moved?(repeat_quantity)
116
+ repeat_quantity > 0.2
58
117
  end
59
118
 
60
- def enough_angle?(index:, quantity:)
119
+ def enough_oneshot_threshold?(index:, quantity:)
61
120
  quantity > threshold(index: index)
62
121
  end
63
122
 
64
- def enough_interval?(index:)
65
- return true if first_time?
66
- return true if (Time.now - last_time) > interval_time(index: index)
67
-
68
- false
69
- end
70
-
71
123
  def threshold(index:)
72
124
  @threshold ||= {}
73
125
  @threshold[index.cache_key] ||= begin
74
- keys_specific = Config::Index.new [*index.keys, 'threshold']
75
- keys_global = Config::Index.new ['threshold', type]
76
- config_value = Config.search(keys_specific) ||
77
- Config.search(keys_global) || 1
78
- BASE_THERESHOLD * config_value
79
- end
80
- end
81
-
82
- def interval_time(index:)
83
- @interval_time ||= {}
84
- @interval_time[index.cache_key] ||= begin
85
- keys_specific = Config::Index.new [*index.keys, 'interval']
86
- keys_global = Config::Index.new ['interval', type]
87
- config_value = Config.search(keys_specific) ||
88
- Config.search(keys_global) || 1
89
- BASE_INTERVAL * config_value
90
- end
126
+ keys_specific = Config::Index.new [*index.keys, 'threshold']
127
+ keys_global = Config::Index.new ['threshold', type]
128
+ config_value = Config.search(keys_specific) ||
129
+ Config.search(keys_global) || 1
130
+ BASE_THERESHOLD * config_value
131
+ end
91
132
  end
92
133
 
93
134
  # direction of gesture
@@ -96,7 +137,7 @@ module Fusuma
96
137
  COUNTERCLOCKWISE = 'counterclockwise'
97
138
 
98
139
  def initialize(angle:)
99
- @angle = angle
140
+ @angle = angle.to_f
100
141
  end
101
142
 
102
143
  def to_s
@@ -104,7 +145,7 @@ module Fusuma
104
145
  end
105
146
 
106
147
  def calc
107
- if @angle > 0
148
+ if @angle.positive?
108
149
  CLOCKWISE
109
150
  else
110
151
  COUNTERCLOCKWISE
@@ -1,48 +1,103 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './detector.rb'
3
+ require_relative './detector'
4
4
 
5
5
  module Fusuma
6
6
  module Plugin
7
7
  module Detectors
8
8
  class SwipeDetector < Detector
9
+ SOURCES = ['gesture'].freeze
9
10
  BUFFER_TYPE = 'gesture'
10
11
  GESTURE_RECORD_TYPE = 'swipe'
11
12
 
12
13
  FINGERS = [3, 4].freeze
13
- BASE_THERESHOLD = 10
14
- BASE_INTERVAL = 0.5
14
+ BASE_THERESHOLD = 25
15
15
 
16
16
  # @param buffers [Array<Buffers::Buffer>]
17
- # @return [Event] if event is detected
17
+ # @return [Events::Event] if event is detected
18
18
  # @return [NilClass] if event is NOT detected
19
19
  def detect(buffers)
20
- buffer = buffers.find { |b| b.type == BUFFER_TYPE }
21
- .select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
22
-
23
- return if buffer.empty?
24
-
25
- move_x = buffer.avg_attrs(:move_x)
26
- move_y = buffer.avg_attrs(:move_y)
27
-
28
- finger = buffer.finger
29
- direction = Direction.new(move_x: move_x.to_f, move_y: move_y.to_f).to_s
30
- quantity = Quantity.new(move_x: move_x.to_f, move_y: move_y.to_f).to_f
31
-
32
- index = create_index(gesture: type,
33
- finger: finger,
34
- direction: direction)
35
-
36
- return unless enough?(index: index, quantity: quantity)
20
+ gesture_buffer = buffers.find { |b| b.type == BUFFER_TYPE }
21
+ .select_from_last_begin
22
+ .select_by_events { |e| e.record.gesture == GESTURE_RECORD_TYPE }
23
+
24
+ updating_events = gesture_buffer.updating_events
25
+ return if updating_events.empty?
26
+
27
+ updating_time = 100 * (updating_events.last.time - updating_events.first.time)
28
+ oneshot_move_x = gesture_buffer.sum_attrs(:move_x) / updating_time
29
+ oneshot_move_y = gesture_buffer.sum_attrs(:move_y) / updating_time
30
+
31
+ finger = gesture_buffer.finger
32
+ status = case gesture_buffer.events.last.record.status
33
+ when 'end'
34
+ 'end'
35
+ when 'update'
36
+ if updating_events.length == 1
37
+ 'begin'
38
+ else
39
+ 'update'
40
+ end
41
+ else
42
+ gesture_buffer.events.last.record.status
43
+ end
44
+
45
+ delta = if status == 'end'
46
+ gesture_buffer.events[-2].record.delta
47
+ else
48
+ gesture_buffer.events.last.record.delta
49
+ end
50
+
51
+ repeat_direction = Direction.new(move_x: delta.move_x, move_y: delta.move_y).to_s
52
+ repeat_quantity = Quantity.new(move_x: delta.move_x, move_y: delta.move_y).to_f
53
+
54
+ repeat_index = create_repeat_index(gesture: type, finger: finger,
55
+ direction: repeat_direction, status: status)
56
+
57
+ if status == 'update'
58
+ return unless moved?(repeat_quantity)
59
+
60
+ oneshot_direction = Direction.new(move_x: oneshot_move_x, move_y: oneshot_move_y).to_s
61
+ oneshot_quantity = Quantity.new(move_x: oneshot_move_x, move_y: oneshot_move_y).to_f
62
+ oneshot_index = create_oneshot_index(gesture: type, finger: finger,
63
+ direction: oneshot_direction)
64
+ if enough_oneshot_threshold?(index: oneshot_index, quantity: oneshot_quantity)
65
+ return [
66
+ create_event(record: Events::Records::IndexRecord.new(
67
+ index: oneshot_index, trigger: :oneshot, args: delta.to_h
68
+ )),
69
+ create_event(record: Events::Records::IndexRecord.new(
70
+ index: repeat_index, trigger: :repeat, args: delta.to_h
71
+ ))
72
+ ]
73
+ end
74
+ end
75
+ create_event(record: Events::Records::IndexRecord.new(
76
+ index: repeat_index, trigger: :repeat, args: delta.to_h
77
+ ))
78
+ end
37
79
 
38
- create_event(record: Events::Records::IndexRecord.new(index: index))
80
+ # @param [String] gesture
81
+ # @param [Integer] finger
82
+ # @param [String] direction
83
+ # @param [String] status
84
+ # @return [Config::Index]
85
+ def create_repeat_index(gesture:, finger:, direction:, status:)
86
+ Config::Index.new(
87
+ [
88
+ Config::Index::Key.new(gesture),
89
+ Config::Index::Key.new(finger.to_i),
90
+ Config::Index::Key.new(direction, skippable: true),
91
+ Config::Index::Key.new(status)
92
+ ]
93
+ )
39
94
  end
40
95
 
41
96
  # @param [String] gesture
42
97
  # @param [Integer] finger
43
98
  # @param [String] direction
44
99
  # @return [Config::Index]
45
- def create_index(gesture:, finger:, direction:)
100
+ def create_oneshot_index(gesture:, finger:, direction:)
46
101
  Config::Index.new(
47
102
  [
48
103
  Config::Index::Key.new(gesture),
@@ -54,41 +109,23 @@ module Fusuma
54
109
 
55
110
  private
56
111
 
57
- def enough?(index:, quantity:)
58
- enough_interval?(index: index) && enough_distance?(index: index, quantity: quantity)
112
+ def moved?(repeat_quantity)
113
+ repeat_quantity > 0.3
59
114
  end
60
115
 
61
- def enough_distance?(index:, quantity:)
116
+ def enough_oneshot_threshold?(index:, quantity:)
62
117
  quantity > threshold(index: index)
63
118
  end
64
119
 
65
- def enough_interval?(index:)
66
- return true if first_time?
67
- return true if (Time.now - last_time) > interval_time(index: index)
68
-
69
- false
70
- end
71
-
72
120
  def threshold(index:)
73
121
  @threshold ||= {}
74
122
  @threshold[index.cache_key] ||= begin
75
- keys_specific = Config::Index.new [*index.keys, 'threshold']
76
- keys_global = Config::Index.new ['threshold', type]
77
- config_value = Config.search(keys_specific) ||
78
- Config.search(keys_global) || 1
79
- BASE_THERESHOLD * config_value
80
- end
81
- end
82
-
83
- def interval_time(index:)
84
- @interval_time ||= {}
85
- @interval_time[index.cache_key] ||= begin
86
- keys_specific = Config::Index.new [*index.keys, 'interval']
87
- keys_global = Config::Index.new ['interval', type]
88
- config_value = Config.search(keys_specific) ||
89
- Config.search(keys_global) || 1
90
- BASE_INTERVAL * config_value
91
- end
123
+ keys_specific = Config::Index.new [*index.keys, 'threshold']
124
+ keys_global = Config::Index.new ['threshold', type]
125
+ config_value = Config.search(keys_specific) ||
126
+ Config.search(keys_global) || 1
127
+ BASE_THERESHOLD * config_value
128
+ end
92
129
  end
93
130
 
94
131
  # direction of gesture
@@ -99,8 +136,8 @@ module Fusuma
99
136
  UP = 'up'
100
137
 
101
138
  def initialize(move_x:, move_y:)
102
- @move_x = move_x
103
- @move_y = move_y
139
+ @move_x = move_x.to_f
140
+ @move_y = move_y.to_f
104
141
  end
105
142
 
106
143
  def to_s
@@ -109,8 +146,8 @@ module Fusuma
109
146
 
110
147
  def calc
111
148
  if @move_x.abs > @move_y.abs
112
- @move_x > 0 ? RIGHT : LEFT
113
- elsif @move_y > 0
149
+ @move_x.positive? ? RIGHT : LEFT
150
+ elsif @move_y.positive?
114
151
  DOWN
115
152
  else
116
153
  UP
@@ -121,8 +158,8 @@ module Fusuma
121
158
  # quantity of gesture
122
159
  class Quantity
123
160
  def initialize(move_x:, move_y:)
124
- @x = move_x.abs
125
- @y = move_y.abs
161
+ @x = move_x.to_f.abs
162
+ @y = move_y.to_f.abs
126
163
  end
127
164
 
128
165
  def to_f
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../base.rb'
4
- require_relative './records/record.rb'
5
- require_relative './records/text_record.rb'
3
+ require_relative '../base'
4
+ require_relative './records/record'
5
+ require_relative './records/text_record'
6
6
 
7
7
  module Fusuma
8
8
  module Plugin
@@ -15,7 +15,8 @@ module Fusuma
15
15
  # @param time [Time]
16
16
  # @param tag [Tag]
17
17
  # @param record [String, Record]
18
- def initialize(time: Time.now, tag:, record:)
18
+ def initialize(tag:, record:, time: Time.now)
19
+ super()
19
20
  @time = time
20
21
  @tag = tag
21
22
  @record = case record
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fusuma
4
+ module Plugin
5
+ module Events
6
+ module Records
7
+ # Context Record
8
+ class ContextRecord < Record
9
+ # define gesture format
10
+ attr_reader :name, :value
11
+
12
+ # @param name [#to_sym]
13
+ # @param value [String]
14
+ def initialize(name:, value:)
15
+ super()
16
+ @name = name.to_sym
17
+ @value = value
18
+ end
19
+
20
+ def type
21
+ :context
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end