fusuma 2.0.0.pre → 2.0.0.pre2

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -5
  3. data/.rubocop_todo.yml +34 -19
  4. data/.solargraph.yml +16 -0
  5. data/.travis.yml +1 -3
  6. data/CHANGELOG.md +1 -1
  7. data/Gemfile +6 -1
  8. data/README.md +43 -5
  9. data/fusuma.gemspec +3 -2
  10. data/lib/fusuma.rb +87 -33
  11. data/lib/fusuma/config.rb +33 -40
  12. data/lib/fusuma/config/index.rb +34 -8
  13. data/lib/fusuma/config/searcher.rb +78 -4
  14. data/lib/fusuma/custom_process.rb +13 -0
  15. data/lib/fusuma/device.rb +19 -6
  16. data/lib/fusuma/environment.rb +3 -3
  17. data/lib/fusuma/hash_support.rb +40 -0
  18. data/lib/fusuma/libinput_command.rb +8 -8
  19. data/lib/fusuma/multi_logger.rb +2 -6
  20. data/lib/fusuma/plugin/base.rb +18 -15
  21. data/lib/fusuma/plugin/buffers/buffer.rb +3 -2
  22. data/lib/fusuma/plugin/buffers/gesture_buffer.rb +34 -25
  23. data/lib/fusuma/plugin/buffers/timer_buffer.rb +3 -3
  24. data/lib/fusuma/plugin/detectors/detector.rb +26 -5
  25. data/lib/fusuma/plugin/detectors/pinch_detector.rb +109 -58
  26. data/lib/fusuma/plugin/detectors/rotate_detector.rb +91 -50
  27. data/lib/fusuma/plugin/detectors/swipe_detector.rb +93 -56
  28. data/lib/fusuma/plugin/events/event.rb +5 -4
  29. data/lib/fusuma/plugin/events/records/context_record.rb +27 -0
  30. data/lib/fusuma/plugin/events/records/gesture_record.rb +9 -6
  31. data/lib/fusuma/plugin/events/records/index_record.rb +46 -14
  32. data/lib/fusuma/plugin/events/records/record.rb +1 -1
  33. data/lib/fusuma/plugin/events/records/text_record.rb +2 -1
  34. data/lib/fusuma/plugin/executors/command_executor.rb +20 -3
  35. data/lib/fusuma/plugin/executors/executor.rb +45 -3
  36. data/lib/fusuma/plugin/filters/filter.rb +1 -1
  37. data/lib/fusuma/plugin/filters/libinput_device_filter.rb +6 -7
  38. data/lib/fusuma/plugin/filters/libinput_timeout_filter.rb +2 -2
  39. data/lib/fusuma/plugin/inputs/input.rb +19 -7
  40. data/lib/fusuma/plugin/inputs/libinput_command_input.rb +10 -5
  41. data/lib/fusuma/plugin/inputs/timer_input.rb +7 -7
  42. data/lib/fusuma/plugin/manager.rb +10 -28
  43. data/lib/fusuma/plugin/parsers/libinput_gesture_parser.rb +10 -8
  44. data/lib/fusuma/plugin/parsers/parser.rb +8 -9
  45. data/lib/fusuma/string_support.rb +16 -0
  46. data/lib/fusuma/version.rb +1 -1
  47. metadata +13 -8
@@ -19,6 +19,11 @@ module Fusuma
19
19
  [Key.new(keys)]
20
20
  end
21
21
  end
22
+
23
+ def inspect
24
+ @keys.map(&:inspect)
25
+ end
26
+
22
27
  attr_reader :keys
23
28
 
24
29
  def cache_key
@@ -32,23 +37,44 @@ module Fusuma
32
37
  end
33
38
  end
34
39
 
40
+ # @return [Index]
41
+ def with_context
42
+ keys = @keys.map do |key|
43
+ next if Searcher.skip? && key.skippable
44
+
45
+ if Searcher.fallback? && key.fallback
46
+ key.fallback
47
+ else
48
+ key
49
+ end
50
+ end
51
+ self.class.new(keys.compact)
52
+ end
53
+
35
54
  # Keys in Index
36
55
  class Key
37
56
  def initialize(symbol_word, skippable: false, fallback: nil)
38
57
  @symbol = begin
39
- symbol_word.to_sym
40
- rescue StandardError
41
- symbol_word
42
- end
58
+ symbol_word.to_sym
59
+ rescue StandardError
60
+ symbol_word
61
+ end
43
62
 
44
63
  @skippable = skippable
45
64
 
46
65
  @fallback = begin
47
- fallback.to_sym
48
- rescue StandardError
49
- fallback
50
- end
66
+ fallback.to_sym
67
+ rescue StandardError
68
+ fallback
69
+ end
51
70
  end
71
+
72
+ def inspect
73
+ skip_marker = @skippable && Searcher.skip? ? '(skip)' : ''
74
+ fallback_marker = @fallback && Searcher.fallback? ? '(fallback)' : ''
75
+ "#{@symbol}#{skip_marker}#{fallback_marker}"
76
+ end
77
+
52
78
  attr_reader :symbol, :skippable, :fallback
53
79
  end
54
80
  end
@@ -6,7 +6,7 @@ module Fusuma
6
6
  # Search config.yml
7
7
  class Searcher
8
8
  def initialize
9
- @cache
9
+ @cache = nil
10
10
  end
11
11
 
12
12
  # @param index [Index]
@@ -31,9 +31,23 @@ module Fusuma
31
31
  value
32
32
  end
33
33
 
34
+ def search_with_context(index, location:, context:)
35
+ return search(index, location: location[0]) if context == {}
36
+
37
+ new_location = location.find do |conf|
38
+ search(index, location: conf) if conf[:context] == context
39
+ end
40
+ search(index, location: new_location)
41
+ end
42
+
43
+ # @param index [Index]
44
+ # @param location [Hash]
45
+ # @return [NilClass]
46
+ # @return [Hash]
47
+ # @return [Object]
34
48
  def search_with_cache(index, location:)
35
- cache([index.cache_key, Searcher.skip?, Searcher.fallback?]) do
36
- search(index, location: location)
49
+ cache([index.cache_key, Searcher.context, Searcher.skip?, Searcher.fallback?]) do
50
+ search_with_context(index, location: location, context: Searcher.context)
37
51
  end
38
52
  end
39
53
 
@@ -56,12 +70,72 @@ module Fusuma
56
70
  def next_location_cadidates(location, key)
57
71
  [
58
72
  location[key.symbol],
59
- Searcher.fallback? && key.fallback && location[key.fallback],
60
73
  Searcher.skip? && key.skippable && location
61
74
  ].compact
62
75
  end
63
76
 
64
77
  class << self
78
+ # @return [Hash]
79
+ def conditions(&block)
80
+ {
81
+ nothing: -> { block.call },
82
+ skip: -> { Config::Searcher.skip { block.call } }
83
+ }
84
+ end
85
+
86
+ # Execute block with specified conditions
87
+ # @param conidtion [Symbol]
88
+ # @return [Object]
89
+ def with_condition(condition, &block)
90
+ conditions(&block)[condition].call
91
+ end
92
+
93
+ # Execute block with all conditions
94
+ # @return [Array<Symbol, Object>]
95
+ def find_condition(&block)
96
+ conditions(&block).find do |c, l|
97
+ result = l.call
98
+ return [c, result] if result
99
+
100
+ nil
101
+ end
102
+ end
103
+
104
+ # Search with context from load_streamed Config
105
+ # @param context [Hash]
106
+ # @return [Object]
107
+ def with_context(context, &block)
108
+ @context = context || {}
109
+ result = block.call
110
+ @context = {}
111
+ result
112
+ end
113
+
114
+ # Return a matching context from config
115
+ # @params request_context [Hash]
116
+ # @return [Hash]
117
+ def find_context(request_context, &block)
118
+ # Search in blocks in the following order.
119
+ # 1. complete match config[:context] == request_context
120
+ # 2. partial match config[:context] =~ request_context
121
+ # 3. no context
122
+ Config.instance.keymap.each do |config|
123
+ next unless config[:context] == request_context
124
+ return config[:context] if with_context(config[:context]) { block.call }
125
+ end
126
+ if request_context.keys.size > 1
127
+ Config.instance.keymap.each do |config|
128
+ next if config[:context].nil?
129
+
130
+ next unless config[:context].all? { |k, v| request_context[k] == v }
131
+ return config[:context] if with_context(config[:context]) { block.call }
132
+ end
133
+ end
134
+ return {} if with_context({}) { block.call }
135
+ end
136
+
137
+ attr_reader :context
138
+
65
139
  def fallback?
66
140
  @fallback
67
141
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fusuma
4
+ # Rename process
5
+ module CustomProcess
6
+ def fork
7
+ Process.fork do
8
+ Process.setproctitle(self.class.name.underscore.to_s)
9
+ yield
10
+ end
11
+ end
12
+ end
13
+ end
data/lib/fusuma/device.rb CHANGED
@@ -1,18 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative './multi_logger'
4
- require_relative './libinput_command.rb'
4
+ require_relative './libinput_command'
5
5
 
6
6
  module Fusuma
7
7
  # detect input device
8
8
  class Device
9
- attr_reader :available
10
- attr_reader :name
11
- attr_reader :id
9
+ attr_reader :id, :name, :capabilities, :available
12
10
 
13
- def initialize(id: nil, name: nil, available: nil)
11
+ def initialize(id: nil, name: nil, capabilities: nil, available: nil)
14
12
  @id = id
15
13
  @name = name
14
+ @capabilities = capabilities
16
15
  @available = available
17
16
  end
18
17
 
@@ -24,6 +23,8 @@ module Fusuma
24
23
  @id = v
25
24
  when :name
26
25
  @name = v
26
+ when :capabilities
27
+ @capabilities = v
27
28
  when :available
28
29
  @available = v
29
30
  end
@@ -31,9 +32,13 @@ module Fusuma
31
32
  end
32
33
 
33
34
  class << self
35
+ # Return devices
36
+ # sort devices by capabilities of gesture
34
37
  # @return [Array]
35
38
  def all
36
- @all ||= fetch_devices
39
+ @all ||= fetch_devices.sort_by do |d|
40
+ d.capabilities.match(/gesture/).to_s
41
+ end
37
42
  end
38
43
 
39
44
  # @raise [SystemExit]
@@ -104,6 +109,8 @@ module Fusuma
104
109
  { id: id }
105
110
  elsif (name = name_from(line))
106
111
  { name: name }
112
+ elsif (capabilities = capabilities_from(line))
113
+ { capabilities: capabilities }
107
114
  elsif (available = available_from(line))
108
115
  { available: available }
109
116
  else
@@ -123,6 +130,12 @@ module Fusuma
123
130
  end
124
131
  end
125
132
 
133
+ def capabilities_from(line)
134
+ line.match('^Capabilities:[[:space:]]*') do |m|
135
+ m.post_match.strip
136
+ end
137
+ end
138
+
126
139
  def available_from(line)
127
140
  # NOTE: is natural scroll available?
128
141
  if line =~ /^Nat.scrolling: /
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './version.rb'
4
- require_relative './libinput_command.rb'
5
- require_relative './multi_logger.rb'
3
+ require_relative './version'
4
+ require_relative './libinput_command'
5
+ require_relative './multi_logger'
6
6
 
7
7
  module Fusuma
8
8
  # Output Environment information
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Patch to hash
4
+ class Hash
5
+ # activesupport-4.1.1/lib/active_support/core_ext/hash/keys.rb
6
+ def deep_symbolize_keys
7
+ deep_transform_keys do |key|
8
+ key.to_sym
9
+ rescue StandardError
10
+ key
11
+ end
12
+ end
13
+
14
+ def deep_transform_keys(&block)
15
+ result = {}
16
+ each do |key, value|
17
+ result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
18
+ end
19
+ result
20
+ end
21
+
22
+ # activesupport/lib/active_support/core_ext/hash/deep_transform_values.rb
23
+ def deep_transform_values(&block)
24
+ _deep_transform_values_in_object(self, &block)
25
+ end
26
+
27
+ private
28
+
29
+ # Support methods for deep transforming nested hashes and arrays.
30
+ def _deep_transform_values_in_object(object, &block)
31
+ case object
32
+ when Hash
33
+ object.transform_values { |value| _deep_transform_values_in_object(value, &block) }
34
+ when Array
35
+ object.map { |e| _deep_transform_values_in_object(e, &block) }
36
+ else
37
+ yield(object)
38
+ end
39
+ end
40
+ end
@@ -30,24 +30,24 @@ module Fusuma
30
30
  end
31
31
 
32
32
  # @yieldparam [String] gives a line in libinput list-devices output to the block
33
- def list_devices
33
+ def list_devices(&block)
34
34
  cmd = list_devices_command
35
35
  MultiLogger.debug(list_devices: cmd)
36
36
  p, i, o, e = POSIX::Spawn.popen4(cmd)
37
37
  i.close
38
- o.each { |line| yield(line) }
38
+ o.each(&block)
39
39
  ensure
40
40
  [i, o, e].each { |io| io.close unless io.closed? }
41
41
  Process.waitpid(p)
42
42
  end
43
43
 
44
- # @return [String] return a latest line libinput debug-events
44
+ # @return [Integer, IO] return a latest line libinput debug-events
45
45
  def debug_events
46
- @debug_events ||= begin
47
- _p, i, o, _e = POSIX::Spawn.popen4(debug_events_with_options)
48
- i.close
49
- o
50
- end
46
+ @debug_events = begin
47
+ p, i, o, _e = POSIX::Spawn.popen4(debug_events_with_options)
48
+ i.close
49
+ [p, o]
50
+ end
51
51
  end
52
52
 
53
53
  # @return [String] command
@@ -12,15 +12,11 @@ module Fusuma
12
12
  attr_accessor :debug_mode
13
13
 
14
14
  def initialize
15
- super(STDOUT)
16
- @err_logger = Logger.new(STDERR)
15
+ super($stdout)
16
+ @err_logger = Logger.new($stderr)
17
17
  @debug_mode = false
18
18
  end
19
19
 
20
- def info(msg)
21
- super(msg)
22
- end
23
-
24
20
  def debug(msg)
25
21
  return unless debug_mode?
26
22
 
@@ -1,22 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './manager.rb'
4
- require_relative '../config.rb'
3
+ require_relative './manager'
4
+ require_relative '../config'
5
+ require_relative '../custom_process'
5
6
 
6
7
  module Fusuma
7
8
  module Plugin
8
9
  # Create a Plugin Class with extending this class
9
10
  class Base
11
+ include CustomProcess
10
12
  # when inherited from subclass
11
13
  def self.inherited(subclass)
14
+ super
12
15
  subclass_path = caller_locations(1..1).first.path
13
16
  Manager.add(plugin_class: subclass, plugin_path: subclass_path)
14
17
  end
15
18
 
16
- # get inherited classes
17
- # @example
18
- # [Vectors::Vector]
19
- # @return [Array]
19
+ # get subclasses
20
+ # @return [Array<Class>]
20
21
  def self.plugins
21
22
  Manager.plugins[name]
22
23
  end
@@ -33,19 +34,21 @@ module Fusuma
33
34
 
34
35
  return params unless key
35
36
 
36
- params.fetch(key, nil).tap do |val|
37
- next if val.nil?
37
+ @config_params ||= {}
38
+ @config_params["#{base.cache_key},#{key}"] ||=
39
+ params.fetch(key, nil).tap do |val|
40
+ next if val.nil?
38
41
 
39
- # NOTE: Type checking for config.yml
40
- param_types = Array(config_param_types.fetch(key))
42
+ # NOTE: Type checking for config.yml
43
+ param_types = Array(config_param_types.fetch(key))
41
44
 
42
- next if param_types.any? { |klass| val.is_a?(klass) }
45
+ next if param_types.any? { |klass| val.is_a?(klass) }
43
46
 
44
- MultiLogger.error('Please fix config.yml.')
45
- MultiLogger.error(":#{base.keys.map(&:symbol)
47
+ MultiLogger.error('Please fix config.yml.')
48
+ MultiLogger.error(":#{base.keys.map(&:symbol)
46
49
  .join(' => :')} => :#{key} should be #{param_types.join(' OR ')}.")
47
- exit 1
48
- end
50
+ exit 1
51
+ end
49
52
  end
50
53
 
51
54
  def config_index
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../base.rb'
3
+ require_relative '../base'
4
4
 
5
5
  module Fusuma
6
6
  module Plugin
@@ -8,6 +8,7 @@ module Fusuma
8
8
  # buffer events and output
9
9
  class Buffer < Base
10
10
  def initialize(*args)
11
+ super()
11
12
  @events = Array.new(*args)
12
13
  end
13
14
 
@@ -15,7 +16,7 @@ module Fusuma
15
16
 
16
17
  # @return [String]
17
18
  def type
18
- self.class.name.underscore.split('/').last.gsub('_buffer', '')
19
+ @type ||= self.class.name.underscore.split('/').last.gsub('_buffer', '')
19
20
  end
20
21
 
21
22
  # @param event [Event]