fusuma 0.11.1 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
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