fusuma 0.11.1 → 1.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +8 -0
  3. data/.gitignore +6 -0
  4. data/.reek.yml +93 -45
  5. data/.rubocop.yml +2 -0
  6. data/.rubocop_todo.yml +16 -75
  7. data/Gemfile +2 -0
  8. data/README.md +12 -5
  9. data/Rakefile +2 -1
  10. data/bin/console +1 -1
  11. data/exe/fusuma +1 -0
  12. data/fusuma.gemspec +9 -2
  13. data/lib/fusuma.rb +88 -31
  14. data/lib/fusuma/config.rb +65 -66
  15. data/lib/fusuma/config/index.rb +49 -0
  16. data/lib/fusuma/device.rb +58 -37
  17. data/lib/fusuma/multi_logger.rb +3 -0
  18. data/lib/fusuma/plugin/base.rb +56 -0
  19. data/lib/fusuma/plugin/buffers/buffer.rb +41 -0
  20. data/lib/fusuma/plugin/buffers/gesture_buffer.rb +70 -0
  21. data/lib/fusuma/plugin/detectors/detector.rb +41 -0
  22. data/lib/fusuma/plugin/detectors/pinch_detector.rb +141 -0
  23. data/lib/fusuma/plugin/detectors/rotate_detector.rb +135 -0
  24. data/lib/fusuma/plugin/detectors/swipe_detector.rb +145 -0
  25. data/lib/fusuma/plugin/events/event.rb +38 -0
  26. data/lib/fusuma/plugin/events/records/gesture_record.rb +31 -0
  27. data/lib/fusuma/plugin/events/records/index_record.rb +53 -0
  28. data/lib/fusuma/plugin/events/records/record.rb +20 -0
  29. data/lib/fusuma/plugin/events/records/text_record.rb +28 -0
  30. data/lib/fusuma/plugin/executors/command_executor.rb +39 -0
  31. data/lib/fusuma/plugin/executors/executor.rb +27 -0
  32. data/lib/fusuma/plugin/filters/filter.rb +40 -0
  33. data/lib/fusuma/plugin/filters/libinput_device_filter.rb +42 -0
  34. data/lib/fusuma/plugin/inputs/input.rb +28 -0
  35. data/lib/fusuma/plugin/inputs/libinput_command_input.rb +133 -0
  36. data/lib/fusuma/plugin/manager.rb +118 -0
  37. data/lib/fusuma/plugin/parsers/libinput_gesture_parser.rb +54 -0
  38. data/lib/fusuma/plugin/parsers/parser.rb +46 -0
  39. data/lib/fusuma/version.rb +3 -1
  40. metadata +74 -14
  41. data/lib/fusuma/command_executor.rb +0 -43
  42. data/lib/fusuma/event_stack.rb +0 -87
  43. data/lib/fusuma/gesture_event.rb +0 -50
  44. data/lib/fusuma/libinput_commands.rb +0 -98
  45. data/lib/fusuma/pinch.rb +0 -58
  46. data/lib/fusuma/swipe.rb +0 -59
@@ -1,50 +0,0 @@
1
- module Fusuma
2
- # pinch or swipe or rotate event
3
- class GestureEvent
4
- def initialize(time, event, finger, directions)
5
- @time = time.to_f
6
- @event = event
7
- @finger = finger
8
- @move_x = directions[:move][:x].to_f
9
- @move_y = directions[:move][:y].to_f
10
- @zoom = directions[:zoom].to_f
11
- end
12
- attr_reader :time, :event, :finger,
13
- :move_x, :move_y, :zoom
14
-
15
- class << self
16
- def initialize_by(line, device_names)
17
- return if device_names.none? do |device_name|
18
- line =~ /^\s?#{device_name}/
19
- end
20
- return if line =~ /_BEGIN/
21
- return unless line =~ /GESTURE_SWIPE|GESTURE_PINCH/
22
- time, event, finger, directions = gesture_event_arguments(line)
23
- MultiLogger.debug(time: time, event: event,
24
- finger: finger, directions: directions)
25
- new(time, event, finger, directions)
26
- end
27
-
28
- private
29
-
30
- def gesture_event_arguments(libinput_line)
31
- event, time, finger, other = parse_libinput(libinput_line)
32
- move_x, move_y, zoom = parse_finger_directions(other)
33
- directions = { move: { x: move_x, y: move_y }, zoom: zoom }
34
- [time, event, finger, directions]
35
- end
36
-
37
- def parse_libinput(line)
38
- _device, event, time, other = line.strip.split(nil, 4)
39
- finger, other = other.split(nil, 2)
40
- [event, time, finger, other]
41
- end
42
-
43
- def parse_finger_directions(line)
44
- return [] unless line
45
- move_x, move_y, _, _, _, zoom = line.tr('/|(|)', ' ').split
46
- [move_x, move_y, zoom]
47
- end
48
- end
49
- end
50
- end
@@ -1,98 +0,0 @@
1
- module Fusuma
2
- # libinput commands wrapper
3
- class LibinputCommands
4
- def initialize(*options)
5
- @options = options
6
- end
7
-
8
- # `libinput-list-devices` and `libinput-debug-events` are deprecated,
9
- # use `libinput list-devices` and `libinput debug-events` from 1.8.
10
- NEW_CLI_OPTION_VERSION = 1.8
11
-
12
- # @return [Boolean]
13
- def new_cli_option_available?
14
- Gem::Version.new(version) >= Gem::Version.new(NEW_CLI_OPTION_VERSION)
15
- end
16
-
17
- # @return [String]
18
- def version
19
- # versiom_command prints "1.6.3\n"
20
- @version ||= `#{version_command}`.strip
21
- end
22
-
23
- # @yield [line] gives a line in libinput list-devices output to the block
24
- def list_devices
25
- cmd = list_devices_command
26
- MultiLogger.debug(debug_events: cmd)
27
- Open3.popen3(cmd) do |_i, o, _e, _w|
28
- o.each { |line| yield(line) }
29
- end
30
- end
31
-
32
- # @yield [line] gives a line in libinput debug-events output to the block
33
- def debug_events
34
- prefix = 'stdbuf -oL --'
35
- options = [*@options, device_option]
36
- cmd = "#{prefix} #{debug_events_command} #{options.join(' ')}".strip
37
- MultiLogger.debug(debug_events: cmd)
38
- Open3.popen3(cmd) do |_i, o, _e, _w|
39
- o.each { |line| yield(line) }
40
- end
41
- end
42
-
43
- # @return [String] command
44
- # @raise [SystemExit]
45
- def version_command
46
- if which('libinput')
47
- 'libinput --version'
48
- elsif which('libinput-list-devices')
49
- 'libinput-list-devices --version'
50
- else
51
- MultiLogger.error 'install libinput-tools'
52
- exit 1
53
- end
54
- end
55
-
56
- def list_devices_command
57
- if new_cli_option_available?
58
- 'libinput list-devices'
59
- else
60
- 'libinput-list-devices'
61
- end
62
- end
63
-
64
- def debug_events_command
65
- if new_cli_option_available?
66
- 'libinput debug-events'
67
- else
68
- 'libinput-debug-events'
69
- end
70
- end
71
-
72
- private
73
-
74
- # use device option only if libinput detect only 1 device
75
- # @return [String]
76
- def device_option
77
- return unless Device.available.size == 1
78
- "--device /dev/input/#{Device.available.first.id}"
79
- end
80
-
81
- # which in ruby: Checking if program exists in $PATH from ruby
82
- # (https://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby)
83
- # Cross-platform way of finding an executable in the $PATH.
84
- #
85
- # which('ruby') #=> /usr/bin/ruby
86
- # @return [String, nil]
87
- def which(command)
88
- exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
89
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
90
- exts.each do |ext|
91
- exe = File.join(path, "#{command}#{ext}")
92
- return exe if File.executable?(exe) && !File.directory?(exe)
93
- end
94
- end
95
- nil
96
- end
97
- end
98
- end
@@ -1,58 +0,0 @@
1
- module Fusuma
2
- # vector data
3
- class Pinch
4
- TYPE = 'pinch'.freeze
5
-
6
- BASE_THERESHOLD = 0.3
7
- BASE_INTERVAL = 0.05
8
-
9
- def initialize(diameter)
10
- @diameter = diameter.to_f
11
- end
12
-
13
- attr_reader :diameter
14
-
15
- def direction
16
- return 'in' if diameter > 0
17
- 'out'
18
- end
19
-
20
- def enough?(trigger)
21
- MultiLogger.debug(diameter: diameter)
22
- enough_diameter?(trigger) && enough_interval?(trigger) &&
23
- self.class.touch_last_time
24
- end
25
-
26
- private
27
-
28
- def enough_diameter?(trigger)
29
- diameter.abs > threshold(trigger)
30
- end
31
-
32
- def enough_interval?(trigger)
33
- return true if first_time?
34
- return true if (Time.now - self.class.last_time) > interval_time(trigger)
35
- false
36
- end
37
-
38
- def first_time?
39
- !self.class.last_time
40
- end
41
-
42
- def threshold(trigger)
43
- @threshold ||= BASE_THERESHOLD * Config.threshold(trigger)
44
- end
45
-
46
- def interval_time(trigger)
47
- @interval_time ||= BASE_INTERVAL * Config.interval(trigger)
48
- end
49
-
50
- class << self
51
- attr_reader :last_time
52
-
53
- def touch_last_time
54
- @last_time = Time.now
55
- end
56
- end
57
- end
58
- end
@@ -1,59 +0,0 @@
1
- module Fusuma
2
- # vector data
3
- class Swipe
4
- TYPE = 'swipe'.freeze
5
-
6
- BASE_THERESHOLD = 20
7
- BASE_INTERVAL = 0.5
8
-
9
- def initialize(move_x, move_y)
10
- @x = move_x
11
- @y = move_y
12
- end
13
- attr_reader :x, :y
14
-
15
- def direction
16
- return x > 0 ? 'right' : 'left' if x.abs > y.abs
17
- y > 0 ? 'down' : 'up'
18
- end
19
-
20
- def enough?(trigger)
21
- MultiLogger.debug(x: x, y: y)
22
- enough_distance?(trigger) && enough_interval?(trigger) &&
23
- self.class.touch_last_time
24
- end
25
-
26
- private
27
-
28
- def enough_distance?(trigger)
29
- threshold = threshold(trigger)
30
- (x.abs > threshold) || (y.abs > threshold)
31
- end
32
-
33
- def enough_interval?(trigger)
34
- return true if first_time?
35
- return true if (Time.now - self.class.last_time) > interval_time(trigger)
36
- false
37
- end
38
-
39
- def first_time?
40
- !self.class.last_time
41
- end
42
-
43
- def threshold(trigger)
44
- @threshold ||= BASE_THERESHOLD * Config.threshold(trigger)
45
- end
46
-
47
- def interval_time(trigger)
48
- @interval_time ||= BASE_INTERVAL * Config.interval(trigger)
49
- end
50
-
51
- class << self
52
- attr_reader :last_time
53
-
54
- def touch_last_time
55
- @last_time = Time.now
56
- end
57
- end
58
- end
59
- end