fusuma 1.11.1 → 2.0.2

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 (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