fusuma 3.9.0 → 3.10.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.
- checksums.yaml +4 -4
- data/README.md +27 -1
- data/lib/fusuma/config/index.rb +12 -7
- data/lib/fusuma/config/searcher.rb +17 -6
- data/lib/fusuma/config/yaml_duplication_checker.rb +6 -0
- data/lib/fusuma/config.rb +17 -3
- data/lib/fusuma/custom_process.rb +3 -0
- data/lib/fusuma/device.rb +15 -0
- data/lib/fusuma/environment.rb +4 -0
- data/lib/fusuma/hash_support.rb +6 -1
- data/lib/fusuma/libinput_command.rb +11 -3
- data/lib/fusuma/multi_logger.rb +15 -4
- data/lib/fusuma/plugin/base.rb +11 -1
- data/lib/fusuma/plugin/buffers/buffer.rb +6 -0
- data/lib/fusuma/plugin/buffers/gesture_buffer.rb +15 -9
- data/lib/fusuma/plugin/buffers/timer_buffer.rb +2 -1
- data/lib/fusuma/plugin/detectors/detector.rb +15 -15
- data/lib/fusuma/plugin/detectors/hold_detector.rb +10 -2
- data/lib/fusuma/plugin/detectors/pinch_detector.rb +13 -1
- data/lib/fusuma/plugin/detectors/rotate_detector.rb +11 -0
- data/lib/fusuma/plugin/detectors/swipe_detector.rb +12 -0
- data/lib/fusuma/plugin/events/event.rb +5 -2
- data/lib/fusuma/plugin/events/records/gesture_record.rb +2 -0
- data/lib/fusuma/plugin/events/records/index_record.rb +2 -0
- data/lib/fusuma/plugin/events/records/record.rb +1 -0
- data/lib/fusuma/plugin/events/records/text_record.rb +3 -0
- data/lib/fusuma/plugin/executors/command_executor.rb +5 -1
- data/lib/fusuma/plugin/executors/executor.rb +6 -0
- data/lib/fusuma/plugin/filters/filter.rb +2 -0
- data/lib/fusuma/plugin/filters/libinput_device_filter.rb +10 -0
- data/lib/fusuma/plugin/inputs/input.rb +5 -0
- data/lib/fusuma/plugin/inputs/libinput_command_input.rb +10 -3
- data/lib/fusuma/plugin/inputs/timer_input.rb +4 -0
- data/lib/fusuma/plugin/manager.rb +11 -1
- data/lib/fusuma/plugin/parsers/libinput_gesture_parser.rb +9 -0
- data/lib/fusuma/plugin/parsers/parser.rb +4 -0
- data/lib/fusuma/string_support.rb +1 -0
- data/lib/fusuma/version.rb +1 -1
- data/lib/fusuma.rb +10 -1
- metadata +2 -2
|
@@ -10,11 +10,13 @@ module Fusuma
|
|
|
10
10
|
# Event format
|
|
11
11
|
class Event < Base
|
|
12
12
|
attr_reader :time
|
|
13
|
-
attr_accessor :tag
|
|
13
|
+
attr_accessor :tag #: String
|
|
14
|
+
attr_accessor :record #: Records::Record
|
|
14
15
|
|
|
15
16
|
# @param time [Time]
|
|
16
17
|
# @param tag [Tag]
|
|
17
18
|
# @param record [String, Record]
|
|
19
|
+
#: (tag: String, record: String | Fusuma::Plugin::Events::Records::Record, ?time: Time | nil) -> void
|
|
18
20
|
def initialize(tag:, record:, time: Time.now)
|
|
19
21
|
super()
|
|
20
22
|
@time = time
|
|
@@ -30,8 +32,9 @@ module Fusuma
|
|
|
30
32
|
end
|
|
31
33
|
end
|
|
32
34
|
|
|
35
|
+
#: () -> String
|
|
33
36
|
def inspect
|
|
34
|
-
"tag: #{tag}, record: #{record}"
|
|
37
|
+
"tag: #{@tag}, record: #{@record}"
|
|
35
38
|
end
|
|
36
39
|
end
|
|
37
40
|
end
|
|
@@ -22,6 +22,7 @@ module Fusuma
|
|
|
22
22
|
# @param gesture [String]
|
|
23
23
|
# @param finger [String, Integer]
|
|
24
24
|
# @param delta [Delta, NilClass]
|
|
25
|
+
#: (status: String, gesture: String, finger: Integer | String, delta: Fusuma::Plugin::Events::Records::GestureRecord::Delta | nil) -> void
|
|
25
26
|
def initialize(status:, gesture:, finger:, delta:)
|
|
26
27
|
super()
|
|
27
28
|
@status = status
|
|
@@ -30,6 +31,7 @@ module Fusuma
|
|
|
30
31
|
@delta = delta
|
|
31
32
|
end
|
|
32
33
|
|
|
34
|
+
#: () -> String
|
|
33
35
|
def to_s
|
|
34
36
|
"#{@gesture}, Finger: #{@finger}, Status: #{@status}"
|
|
35
37
|
end
|
|
@@ -14,6 +14,7 @@ module Fusuma
|
|
|
14
14
|
# @param [Config::Index] index
|
|
15
15
|
# @param [Symbol] position [:prefix, :body, :surfix]
|
|
16
16
|
# @param [Symbol] trigger [:oneshot, :repeat]
|
|
17
|
+
#: (index: Fusuma::Config::Index, ?position: Symbol, ?trigger: Symbol, ?args: Hash[untyped, untyped]) -> void
|
|
17
18
|
def initialize(index:, position: :body, trigger: :oneshot, args: {})
|
|
18
19
|
super()
|
|
19
20
|
@index = index
|
|
@@ -26,6 +27,7 @@ module Fusuma
|
|
|
26
27
|
"#{@index}, #{@position}, #{@trigger}, #{@args}"
|
|
27
28
|
end
|
|
28
29
|
|
|
30
|
+
#: () -> Symbol
|
|
29
31
|
def type
|
|
30
32
|
:index
|
|
31
33
|
end
|
|
@@ -9,16 +9,19 @@ module Fusuma
|
|
|
9
9
|
# Default Record
|
|
10
10
|
class TextRecord < Record
|
|
11
11
|
# @param text [String]
|
|
12
|
+
#: (String) -> void
|
|
12
13
|
def initialize(text)
|
|
13
14
|
super()
|
|
14
15
|
@text = text
|
|
15
16
|
end
|
|
16
17
|
|
|
18
|
+
#: () -> Symbol
|
|
17
19
|
def type
|
|
18
20
|
:text
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
# @return [String]
|
|
24
|
+
#: () -> String
|
|
22
25
|
def to_s
|
|
23
26
|
@text
|
|
24
27
|
end
|
|
@@ -13,6 +13,7 @@ module Fusuma
|
|
|
13
13
|
[:command]
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
#: (Fusuma::Plugin::Events::Event) -> void
|
|
16
17
|
def execute(event)
|
|
17
18
|
command = search_command(event)
|
|
18
19
|
|
|
@@ -26,9 +27,10 @@ module Fusuma
|
|
|
26
27
|
pid = Process.spawn(additional_env, command.to_s)
|
|
27
28
|
Process.detach(pid)
|
|
28
29
|
rescue SystemCallError => e
|
|
29
|
-
MultiLogger.error("#{event.record.index.keys}
|
|
30
|
+
MultiLogger.error("#{event.record.index.keys}: #{e.message}")
|
|
30
31
|
end
|
|
31
32
|
|
|
33
|
+
#: (Fusuma::Plugin::Events::Event) -> (String | bool)
|
|
32
34
|
def executable?(event)
|
|
33
35
|
event.tag.end_with?("_detector") &&
|
|
34
36
|
event.record.type == :index &&
|
|
@@ -37,6 +39,7 @@ module Fusuma
|
|
|
37
39
|
|
|
38
40
|
# @param event [Event]
|
|
39
41
|
# @return [String]
|
|
42
|
+
#: (Fusuma::Plugin::Events::Event) -> String
|
|
40
43
|
def search_command(event)
|
|
41
44
|
command_index = Config::Index.new([*event.record.index.keys, :command])
|
|
42
45
|
Config.instance.search(command_index)
|
|
@@ -44,6 +47,7 @@ module Fusuma
|
|
|
44
47
|
|
|
45
48
|
# @param event [Event]
|
|
46
49
|
# @return [Float]
|
|
50
|
+
#: (Fusuma::Plugin::Events::Event) -> Float
|
|
47
51
|
def args_accel(event)
|
|
48
52
|
accel_index = Config::Index.new([*event.record.index.keys, :accel])
|
|
49
53
|
(Config.instance.search(accel_index) || 1).to_f
|
|
@@ -13,6 +13,7 @@ module Fusuma
|
|
|
13
13
|
|
|
14
14
|
# Executor parameter on config.yml
|
|
15
15
|
# @return [Array<Symbol>]
|
|
16
|
+
#: () -> nil
|
|
16
17
|
def execute_keys
|
|
17
18
|
# [name.split('Executors::').last.underscore.gsub('_executor', '').to_sym]
|
|
18
19
|
raise NotImplementedError, "override #{self.class.name}##{__method__}"
|
|
@@ -21,12 +22,14 @@ module Fusuma
|
|
|
21
22
|
# check executable
|
|
22
23
|
# @param _event [Events::Event]
|
|
23
24
|
# @return [TrueClass, FalseClass]
|
|
25
|
+
#: (String) -> nil
|
|
24
26
|
def executable?(_event)
|
|
25
27
|
raise NotImplementedError, "override #{self.class.name}##{__method__}"
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
# @param event [Events::Event]
|
|
29
31
|
# @return [TrueClass, FalseClass]
|
|
32
|
+
#: (Fusuma::Plugin::Events::Event) -> bool
|
|
30
33
|
def enough_interval?(event)
|
|
31
34
|
return true if event.record.index.keys.any? { |key| key.symbol == :end }
|
|
32
35
|
|
|
@@ -35,10 +38,12 @@ module Fusuma
|
|
|
35
38
|
true
|
|
36
39
|
end
|
|
37
40
|
|
|
41
|
+
#: (Fusuma::Plugin::Events::Event) -> Time
|
|
38
42
|
def update_interval(event)
|
|
39
43
|
@wait_until = event.time + interval(event).to_f
|
|
40
44
|
end
|
|
41
45
|
|
|
46
|
+
#: (Fusuma::Plugin::Events::Event) -> Float
|
|
42
47
|
def interval(event)
|
|
43
48
|
@interval_time ||= {}
|
|
44
49
|
index = event.record.index
|
|
@@ -57,6 +62,7 @@ module Fusuma
|
|
|
57
62
|
# execute something
|
|
58
63
|
# @param _event [Event]
|
|
59
64
|
# @return [nil]
|
|
65
|
+
#: (String) -> nil
|
|
60
66
|
def execute(_event)
|
|
61
67
|
raise NotImplementedError, "override #{self.class.name}##{__method__}"
|
|
62
68
|
end
|
|
@@ -11,6 +11,7 @@ module Fusuma
|
|
|
11
11
|
# @param event [Event]
|
|
12
12
|
# @return [Event] when keeping event
|
|
13
13
|
# @return [NilClass] when discarding record
|
|
14
|
+
#: (Fusuma::Plugin::Events::Event) -> Fusuma::Plugin::Events::Event?
|
|
14
15
|
def filter(event)
|
|
15
16
|
return event if !/#{source}/.match?(event.tag)
|
|
16
17
|
|
|
@@ -29,6 +30,7 @@ module Fusuma
|
|
|
29
30
|
|
|
30
31
|
# Set source for tag from config.yml.
|
|
31
32
|
# DEFAULT_SOURCE is defined in each Filter plugins.
|
|
33
|
+
#: () -> String
|
|
32
34
|
def source
|
|
33
35
|
@source ||= config_params(:source) || self.class.const_get(:DEFAULT_SOURCE)
|
|
34
36
|
end
|
|
@@ -10,6 +10,7 @@ module Fusuma
|
|
|
10
10
|
class LibinputDeviceFilter < Filter
|
|
11
11
|
DEFAULT_SOURCE = "libinput_command_input"
|
|
12
12
|
|
|
13
|
+
#: () -> Hash[Symbol, Array[Class] | Class]
|
|
13
14
|
def config_param_types
|
|
14
15
|
{
|
|
15
16
|
source: String,
|
|
@@ -19,6 +20,7 @@ module Fusuma
|
|
|
19
20
|
|
|
20
21
|
# @return [TrueClass] when keeping it
|
|
21
22
|
# @return [FalseClass] when discarding it
|
|
23
|
+
#: (Fusuma::Plugin::Events::Records::TextRecord) -> bool
|
|
22
24
|
def keep?(record)
|
|
23
25
|
# NOTE: purge cache when found new device
|
|
24
26
|
if record.to_s =~ /\sDEVICE_ADDED\s/ && keep_device.match_pattern?(record.to_s)
|
|
@@ -29,6 +31,7 @@ module Fusuma
|
|
|
29
31
|
keep_device.all.map(&:id).include?(device_id)
|
|
30
32
|
end
|
|
31
33
|
|
|
34
|
+
#: () -> Fusuma::Plugin::Filters::LibinputDeviceFilter::KeepDevice
|
|
32
35
|
def keep_device
|
|
33
36
|
@keep_device ||= begin
|
|
34
37
|
from_config = Array(config_params(:keep_device_names))
|
|
@@ -36,6 +39,7 @@ module Fusuma
|
|
|
36
39
|
end
|
|
37
40
|
end
|
|
38
41
|
|
|
42
|
+
#: () -> String
|
|
39
43
|
def config_param_sample
|
|
40
44
|
<<~SAMPLE
|
|
41
45
|
```config.yml
|
|
@@ -50,6 +54,7 @@ module Fusuma
|
|
|
50
54
|
|
|
51
55
|
# Select Device to keep
|
|
52
56
|
class KeepDevice
|
|
57
|
+
#: (name_patterns: Array[untyped]) -> void
|
|
53
58
|
def initialize(name_patterns:)
|
|
54
59
|
@name_patterns = name_patterns | Array(self.class.from_option)
|
|
55
60
|
end
|
|
@@ -57,12 +62,14 @@ module Fusuma
|
|
|
57
62
|
attr_reader :name_patterns
|
|
58
63
|
|
|
59
64
|
# remove cache for reloading new devices
|
|
65
|
+
#: () -> void
|
|
60
66
|
def reset
|
|
61
67
|
@all = nil
|
|
62
68
|
Device.reset
|
|
63
69
|
end
|
|
64
70
|
|
|
65
71
|
# @return [Array]
|
|
72
|
+
#: () -> Array[Device]
|
|
66
73
|
def all
|
|
67
74
|
@all ||= if @name_patterns.empty?
|
|
68
75
|
Device.available
|
|
@@ -75,6 +82,7 @@ module Fusuma
|
|
|
75
82
|
end
|
|
76
83
|
end
|
|
77
84
|
|
|
85
|
+
#: () -> nil
|
|
78
86
|
def print_not_found_messages
|
|
79
87
|
puts "Device is not found. Check following section on your config.yml"
|
|
80
88
|
puts LibinputDeviceFilter.new.config_param_sample
|
|
@@ -82,6 +90,7 @@ module Fusuma
|
|
|
82
90
|
|
|
83
91
|
# @return [TrueClass]
|
|
84
92
|
# @return [FalseClass]
|
|
93
|
+
#: (String) -> bool
|
|
85
94
|
def match_pattern?(string)
|
|
86
95
|
return true if @name_patterns.empty?
|
|
87
96
|
|
|
@@ -92,6 +101,7 @@ module Fusuma
|
|
|
92
101
|
attr_reader :from_option
|
|
93
102
|
|
|
94
103
|
# TODO: remove from_option and command line options
|
|
104
|
+
#: (nil) -> void
|
|
95
105
|
def from_option=(device)
|
|
96
106
|
if device
|
|
97
107
|
warn <<~COMMENT
|
|
@@ -9,6 +9,7 @@ module Fusuma
|
|
|
9
9
|
# Inherite this base
|
|
10
10
|
# @abstract Subclass and override {#io} to implement
|
|
11
11
|
class Input < Base
|
|
12
|
+
#: (*nil) -> void
|
|
12
13
|
def initialize(*args)
|
|
13
14
|
super
|
|
14
15
|
@tag = self.class.name.split("Inputs::").last.underscore
|
|
@@ -19,6 +20,7 @@ module Fusuma
|
|
|
19
20
|
# Wait multiple inputs until it becomes readable
|
|
20
21
|
# @param inputs [Array<Input>]
|
|
21
22
|
# @return [Event]
|
|
23
|
+
#: (Array[untyped]) -> Fusuma::Plugin::Events::Event
|
|
22
24
|
def self.select(inputs)
|
|
23
25
|
ios = IO.select(inputs.map(&:io))
|
|
24
26
|
io = ios&.first&.first
|
|
@@ -32,6 +34,7 @@ module Fusuma
|
|
|
32
34
|
# IO#readline is blocking method
|
|
33
35
|
# so input plugin must write line to pipe (include `\n`)
|
|
34
36
|
# or, override read_from_io and implement your own read method
|
|
37
|
+
#: () -> String
|
|
35
38
|
def read_from_io
|
|
36
39
|
io.readline(chomp: true)
|
|
37
40
|
rescue EOFError => e
|
|
@@ -44,11 +47,13 @@ module Fusuma
|
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
# @return [IO]
|
|
50
|
+
#: () -> nil
|
|
47
51
|
def io
|
|
48
52
|
raise NotImplementedError, "override #{self.class.name}##{__method__}"
|
|
49
53
|
end
|
|
50
54
|
|
|
51
55
|
# @return [Event]
|
|
56
|
+
#: (?record: String) -> Fusuma::Plugin::Events::Event
|
|
52
57
|
def create_event(record: "dummy input")
|
|
53
58
|
e = Events::Event.new(tag: tag, record: record)
|
|
54
59
|
MultiLogger.debug(input_event: e)
|
|
@@ -8,6 +8,7 @@ module Fusuma
|
|
|
8
8
|
module Inputs
|
|
9
9
|
# libinput commands wrapper
|
|
10
10
|
class LibinputCommandInput < Input
|
|
11
|
+
#: () -> Hash[Symbol, Array[Class]]
|
|
11
12
|
def config_param_types
|
|
12
13
|
{
|
|
13
14
|
device: [String],
|
|
@@ -23,6 +24,7 @@ module Fusuma
|
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
# @return [IO]
|
|
27
|
+
#: () -> StringIO
|
|
26
28
|
def io
|
|
27
29
|
@io ||= begin
|
|
28
30
|
reader, writer = create_io
|
|
@@ -32,6 +34,7 @@ module Fusuma
|
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
# @return [LibinputCommand]
|
|
37
|
+
#: () -> (Fusuma::LibinputCommand)
|
|
35
38
|
def command
|
|
36
39
|
@command ||= LibinputCommand.new(
|
|
37
40
|
libinput_options: libinput_options,
|
|
@@ -44,6 +47,7 @@ module Fusuma
|
|
|
44
47
|
end
|
|
45
48
|
|
|
46
49
|
# @return [Array]
|
|
50
|
+
#: () -> Array[String]
|
|
47
51
|
def libinput_options
|
|
48
52
|
device = ("--device='#{config_params(:device)}'" if config_params(:device))
|
|
49
53
|
enable_tap = "--enable-tap" if config_params(:"enable-tap")
|
|
@@ -61,15 +65,18 @@ module Fusuma
|
|
|
61
65
|
].compact
|
|
62
66
|
end
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
#: () -> String?
|
|
69
|
+
def libinput_command # steep:ignore MethodBodyTypeMismatch
|
|
65
70
|
config_params(:"libinput-command")
|
|
66
71
|
end
|
|
67
72
|
|
|
68
|
-
|
|
73
|
+
#: () -> String?
|
|
74
|
+
def debug_events_command # steep:ignore MethodBodyTypeMismatch
|
|
69
75
|
config_params(:"libinput-debug-events")
|
|
70
76
|
end
|
|
71
77
|
|
|
72
|
-
|
|
78
|
+
#: () -> String?
|
|
79
|
+
def list_devices_command # steep:ignore MethodBodyTypeMismatch
|
|
73
80
|
config_params(:"libinput-list-devices")
|
|
74
81
|
end
|
|
75
82
|
|
|
@@ -17,6 +17,7 @@ module Fusuma
|
|
|
17
17
|
}
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
#: (*nil, ?interval: nil) -> void
|
|
20
21
|
def initialize(*args, interval: nil)
|
|
21
22
|
super(*args)
|
|
22
23
|
@interval = interval || config_params(:interval) || DEFAULT_INTERVAL
|
|
@@ -25,6 +26,7 @@ module Fusuma
|
|
|
25
26
|
|
|
26
27
|
attr_reader :interval
|
|
27
28
|
|
|
29
|
+
#: () -> IO
|
|
28
30
|
def io
|
|
29
31
|
@io ||= begin
|
|
30
32
|
reader, writer = create_io
|
|
@@ -34,6 +36,7 @@ module Fusuma
|
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
38
|
|
|
39
|
+
#: (IO, IO) -> Thread
|
|
37
40
|
def start(reader, writer)
|
|
38
41
|
Thread.new do
|
|
39
42
|
timer_loop(writer)
|
|
@@ -62,6 +65,7 @@ module Fusuma
|
|
|
62
65
|
MultiLogger.error e
|
|
63
66
|
end
|
|
64
67
|
|
|
68
|
+
#: (Time) -> Thread::Queue
|
|
65
69
|
def wake_early(t)
|
|
66
70
|
@early_wake_queue.push(t + EPSILON_TIME)
|
|
67
71
|
end
|
|
@@ -7,22 +7,27 @@ module Fusuma
|
|
|
7
7
|
module Plugin
|
|
8
8
|
# Manage Fusuma plugins
|
|
9
9
|
class Manager
|
|
10
|
+
#: (Class) -> void
|
|
10
11
|
def initialize(plugin_class)
|
|
11
12
|
@plugin_class = plugin_class
|
|
12
13
|
end
|
|
13
14
|
|
|
15
|
+
#: () -> Array[untyped]
|
|
14
16
|
def require_siblings_from_plugin_dir
|
|
15
17
|
fusuma_default_plugin_paths.each { |siblings_plugin| require(siblings_plugin) }
|
|
16
18
|
end
|
|
17
19
|
|
|
20
|
+
#: () -> Array[untyped]
|
|
18
21
|
def require_siblings_from_gems
|
|
19
22
|
fusuma_external_plugin_paths.each { |siblings_plugin| require(siblings_plugin) }
|
|
20
23
|
end
|
|
21
24
|
|
|
25
|
+
#: () -> Regexp
|
|
22
26
|
def exclude_path_pattern
|
|
23
27
|
%r{fusuma/plugin/[^/]*.rb}
|
|
24
28
|
end
|
|
25
29
|
|
|
30
|
+
#: () -> Array[untyped]
|
|
26
31
|
def fusuma_default_plugin_paths
|
|
27
32
|
@_fusuma_default_plugin_paths ||= Dir.glob(File.expand_path("#{__dir__}/../../#{search_key}")).grep_v(exclude_path_pattern).sort
|
|
28
33
|
end
|
|
@@ -38,7 +43,7 @@ module Fusuma
|
|
|
38
43
|
raise "Not Found: #{match_data[1]}/#{match_data[2]}/*.gemspec" unless plugin_gemspec_path
|
|
39
44
|
|
|
40
45
|
plugin_gemspec = Gem::Specification.load(plugin_gemspec_path)
|
|
41
|
-
fusuma_gemspec_path = File.expand_path("../../../fusuma.gemspec", __dir__)
|
|
46
|
+
fusuma_gemspec_path = File.expand_path("../../../fusuma.gemspec", __dir__ || ".")
|
|
42
47
|
fusuma_gemspec = Gem::Specification.load(fusuma_gemspec_path)
|
|
43
48
|
next if plugin_gemspec == fusuma_gemspec
|
|
44
49
|
|
|
@@ -56,6 +61,7 @@ module Fusuma
|
|
|
56
61
|
# @example
|
|
57
62
|
# search_key
|
|
58
63
|
# => "fusuma/plugin/detectors/*rb"
|
|
64
|
+
#: () -> String
|
|
59
65
|
def search_key
|
|
60
66
|
File.join(plugin_dir_name, "*rb")
|
|
61
67
|
end
|
|
@@ -66,6 +72,7 @@ module Fusuma
|
|
|
66
72
|
# plugin_dir_name
|
|
67
73
|
# => "fusuma/plugin/detectors"
|
|
68
74
|
# @return [String]
|
|
75
|
+
#: () -> String
|
|
69
76
|
def plugin_dir_name
|
|
70
77
|
@plugin_class.name.match(/(Fusuma::.*)::/)[1].to_s.underscore
|
|
71
78
|
end
|
|
@@ -101,6 +108,7 @@ module Fusuma
|
|
|
101
108
|
manager.require_siblings_from_gems
|
|
102
109
|
end
|
|
103
110
|
|
|
111
|
+
#: () -> void
|
|
104
112
|
def require_base_plugins
|
|
105
113
|
require_relative "base"
|
|
106
114
|
require_relative "events/event"
|
|
@@ -112,6 +120,7 @@ module Fusuma
|
|
|
112
120
|
require_relative "executors/executor"
|
|
113
121
|
end
|
|
114
122
|
|
|
123
|
+
#: () -> Hash[untyped, untyped]
|
|
115
124
|
def plugins
|
|
116
125
|
@plugins ||= {}
|
|
117
126
|
end
|
|
@@ -122,6 +131,7 @@ module Fusuma
|
|
|
122
131
|
# => ["/path/to/fusuma/lib/fusuma/plugin/inputs/input.rb",
|
|
123
132
|
# "/path/to/fusuma/lib/fusuma/plugin/inputs/libinput_command_input.rb",
|
|
124
133
|
# "/path/to/fusuma/lib/fusuma/plugin/inputs/timer_input.rb"]
|
|
134
|
+
#: () -> Array[untyped]
|
|
125
135
|
def load_paths
|
|
126
136
|
@load_paths ||= []
|
|
127
137
|
end
|
|
@@ -13,6 +13,7 @@ module Fusuma
|
|
|
13
13
|
|
|
14
14
|
# @param record [String]
|
|
15
15
|
# @return [Records::GestureRecord, nil]
|
|
16
|
+
#: (Fusuma::Plugin::Events::Records::TextRecord) -> Fusuma::Plugin::Events::Records::GestureRecord?
|
|
16
17
|
def parse_record(record)
|
|
17
18
|
case line = record.to_s
|
|
18
19
|
when /GESTURE_SWIPE|GESTURE_PINCH|GESTURE_HOLD/
|
|
@@ -29,6 +30,7 @@ module Fusuma
|
|
|
29
30
|
|
|
30
31
|
private
|
|
31
32
|
|
|
33
|
+
#: (String) -> Array[untyped]
|
|
32
34
|
def parse_libinput(line)
|
|
33
35
|
if libinput_1_27_0_or_later?
|
|
34
36
|
parse_line_1_27_0_or_later(line)
|
|
@@ -37,16 +39,19 @@ module Fusuma
|
|
|
37
39
|
end
|
|
38
40
|
end
|
|
39
41
|
|
|
42
|
+
#: () -> bool
|
|
40
43
|
def libinput_1_27_0_or_later?
|
|
41
44
|
return @libinput_1_27_0_or_later if defined?(@libinput_1_27_0_or_later)
|
|
42
45
|
|
|
43
46
|
@libinput_1_27_0_or_later = Inputs::LibinputCommandInput.new.command.libinput_1_27_0_or_later?
|
|
44
47
|
end
|
|
45
48
|
|
|
49
|
+
#: (String) -> Array[untyped]
|
|
46
50
|
def parse_line(line)
|
|
47
51
|
_device, event_name, _time, other = line.strip.split(nil, 4)
|
|
48
52
|
finger, other = other.split(nil, 2)
|
|
49
53
|
|
|
54
|
+
return [] unless event_name
|
|
50
55
|
gesture, status = *detect_gesture(event_name)
|
|
51
56
|
|
|
52
57
|
status = "cancelled" if gesture == "hold" && status == "end" && other == "cancelled"
|
|
@@ -54,6 +59,7 @@ module Fusuma
|
|
|
54
59
|
[gesture, status, finger, delta]
|
|
55
60
|
end
|
|
56
61
|
|
|
62
|
+
#: (String) -> Array[untyped]
|
|
57
63
|
def parse_line_1_27_0_or_later(line)
|
|
58
64
|
_device, event_name, other = line.strip.split(nil, 3)
|
|
59
65
|
|
|
@@ -63,6 +69,7 @@ module Fusuma
|
|
|
63
69
|
|
|
64
70
|
_time, finger, other = other.split(nil, 3)
|
|
65
71
|
|
|
72
|
+
return [] unless event_name
|
|
66
73
|
gesture, status = *detect_gesture(event_name)
|
|
67
74
|
|
|
68
75
|
status = "cancelled" if gesture == "hold" && status == "end" && other == "cancelled"
|
|
@@ -70,6 +77,7 @@ module Fusuma
|
|
|
70
77
|
[gesture, status, finger, delta]
|
|
71
78
|
end
|
|
72
79
|
|
|
80
|
+
#: (String) -> Array[untyped]
|
|
73
81
|
def detect_gesture(event_name)
|
|
74
82
|
event_name =~ /GESTURE_(SWIPE|PINCH|HOLD)_(BEGIN|UPDATE|END)/
|
|
75
83
|
gesture = Regexp.last_match(1).downcase
|
|
@@ -77,6 +85,7 @@ module Fusuma
|
|
|
77
85
|
[gesture, status]
|
|
78
86
|
end
|
|
79
87
|
|
|
88
|
+
#: (String?) -> Fusuma::Plugin::Events::Records::GestureRecord::Delta?
|
|
80
89
|
def parse_delta(line)
|
|
81
90
|
return if line.nil?
|
|
82
91
|
|
|
@@ -12,6 +12,7 @@ module Fusuma
|
|
|
12
12
|
# if `#parse_record` return nil, this method will return original event
|
|
13
13
|
# @param event [Event]
|
|
14
14
|
# @return [Event]
|
|
15
|
+
#: (Fusuma::Plugin::Events::Event) -> Fusuma::Plugin::Events::Event
|
|
15
16
|
def parse(event)
|
|
16
17
|
return event if event.tag != source
|
|
17
18
|
|
|
@@ -25,10 +26,12 @@ module Fusuma
|
|
|
25
26
|
|
|
26
27
|
# Set source for tag from config.yml.
|
|
27
28
|
# DEFAULT_SOURCE is defined in each Parser plugins.
|
|
29
|
+
#: () -> String
|
|
28
30
|
def source
|
|
29
31
|
@source ||= config_params(:source) || self.class.const_get(:DEFAULT_SOURCE)
|
|
30
32
|
end
|
|
31
33
|
|
|
34
|
+
#: () -> String
|
|
32
35
|
def tag
|
|
33
36
|
@tag ||= self.class.name.split("::").last.underscore
|
|
34
37
|
end
|
|
@@ -36,6 +39,7 @@ module Fusuma
|
|
|
36
39
|
# parse Record object
|
|
37
40
|
# @param _record [Record]
|
|
38
41
|
# @return [Record, nil]
|
|
42
|
+
#: (Fusuma::Plugin::Events::Records::Record) -> Fusuma::Plugin::Events::Records::Record?
|
|
39
43
|
def parse_record(_record)
|
|
40
44
|
nil
|
|
41
45
|
end
|
data/lib/fusuma/version.rb
CHANGED
data/lib/fusuma.rb
CHANGED
|
@@ -12,6 +12,7 @@ module Fusuma
|
|
|
12
12
|
# main class
|
|
13
13
|
class Runner
|
|
14
14
|
class << self
|
|
15
|
+
#: (?Hash[untyped, untyped]) -> void
|
|
15
16
|
def run(option = {})
|
|
16
17
|
read_options(option)
|
|
17
18
|
instance = new
|
|
@@ -26,6 +27,7 @@ module Fusuma
|
|
|
26
27
|
|
|
27
28
|
private
|
|
28
29
|
|
|
30
|
+
#: (Hash[untyped, untyped]) -> void
|
|
29
31
|
def read_options(option)
|
|
30
32
|
MultiLogger.filepath = option[:log_filepath]
|
|
31
33
|
MultiLogger.instance.debug_mode = option[:verbose]
|
|
@@ -53,13 +55,17 @@ module Fusuma
|
|
|
53
55
|
Process.daemon if option[:daemon]
|
|
54
56
|
end
|
|
55
57
|
|
|
58
|
+
#: (String?) -> void
|
|
56
59
|
def load_custom_config(config_path = nil)
|
|
57
60
|
Config.custom_path = config_path
|
|
58
61
|
end
|
|
59
62
|
end
|
|
60
63
|
|
|
61
|
-
|
|
64
|
+
#: () -> void
|
|
65
|
+
def initialize
|
|
66
|
+
end
|
|
62
67
|
|
|
68
|
+
#: () -> void
|
|
63
69
|
def initialize_plugins
|
|
64
70
|
@inputs = Plugin::Inputs::Input.plugins.map do |cls|
|
|
65
71
|
cls.ancestors.include?(Singleton) ? cls.instance : cls.new
|
|
@@ -71,6 +77,7 @@ module Fusuma
|
|
|
71
77
|
@executors = Plugin::Executors::Executor.plugins.map(&:new)
|
|
72
78
|
end
|
|
73
79
|
|
|
80
|
+
#: () -> void
|
|
74
81
|
def run
|
|
75
82
|
loop { pipeline }
|
|
76
83
|
end
|
|
@@ -193,6 +200,7 @@ module Fusuma
|
|
|
193
200
|
@buffers.each(&:clear_expired)
|
|
194
201
|
end
|
|
195
202
|
|
|
203
|
+
#: () -> void
|
|
196
204
|
def set_trap
|
|
197
205
|
Signal.trap("INT") {
|
|
198
206
|
shutdown
|
|
@@ -206,6 +214,7 @@ module Fusuma
|
|
|
206
214
|
|
|
207
215
|
private
|
|
208
216
|
|
|
217
|
+
#: () -> Array[untyped]
|
|
209
218
|
def shutdown
|
|
210
219
|
[@inputs, @filters, @parsers, @buffers, @detectors, @executors].flatten.compact.each do |plugin|
|
|
211
220
|
plugin.shutdown
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fusuma
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- iberianpig
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-10-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Fusuma is multitouch gesture recognizer. This gem makes your touchpad
|
|
14
14
|
on Linux able to recognize swipes or pinchs and assign command to them. Read installation
|