procon_bypass_man 0.1.3 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +40 -13
- data/.rubocop.yml +24 -0
- data/CHANGELOG.md +20 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +26 -3
- data/README.md +13 -10
- data/docs/setup_raspi.md +12 -7
- data/docs/setup_raspi.mitamae.rb +45 -0
- data/docs/setup_raspi_by_mitamae.md +13 -0
- data/examples/practical/app.rb +3 -2
- data/examples/practical/setting.yml +1 -1
- data/lib/procon_bypass_man/configuration/layer.rb +39 -10
- data/lib/procon_bypass_man/configuration/loader.rb +17 -16
- data/lib/procon_bypass_man/configuration/validator.rb +70 -5
- data/lib/procon_bypass_man/configuration.rb +10 -7
- data/lib/procon_bypass_man/device_connector.rb +336 -0
- data/lib/procon_bypass_man/procon/button_collection.rb +3 -2
- data/lib/procon_bypass_man/procon/flip_cache.rb +22 -0
- data/lib/procon_bypass_man/procon/pressed_button_helper.rb +1 -1
- data/lib/procon_bypass_man/procon.rb +20 -11
- data/lib/procon_bypass_man/runner.rb +24 -42
- data/lib/procon_bypass_man/timer.rb +14 -0
- data/lib/procon_bypass_man/version.rb +1 -1
- data/lib/procon_bypass_man.rb +25 -8
- data/procon_bypass_man.gemspec +1 -1
- data/project_template/README.md +16 -0
- data/project_template/app.rb +18 -0
- data/project_template/setting.yml +35 -0
- data/project_template/systemd_units/pbm.service +13 -0
- metadata +15 -8
- data/examples/pbm.service +0 -27
- data/examples/simple.rb +0 -13
- data/lib/procon_bypass_man/device_registry.rb +0 -42
@@ -24,7 +24,7 @@ class ProconBypassMan::Procon::ButtonCollection
|
|
24
24
|
3 => [:zr, :r, :sr, :sl, :a, :b, :x, :y],
|
25
25
|
4 => [:grip, :_undefined_key, :cap, :home, :thumbl, :thumbr, :plus, :minus],
|
26
26
|
5 => [:zl, :l, :sl, :sr, :left, :right, :up, :down],
|
27
|
-
}
|
27
|
+
}.freeze
|
28
28
|
|
29
29
|
BUTTONS_MAP = BYTES_MAP.reduce({}) { |acc, value|
|
30
30
|
next acc if value[1].nil?
|
@@ -32,7 +32,8 @@ class ProconBypassMan::Procon::ButtonCollection
|
|
32
32
|
acc[button] = { byte_position: value[0], bit_position: index }
|
33
33
|
end
|
34
34
|
acc
|
35
|
-
}
|
35
|
+
}.freeze
|
36
|
+
BUTTONS = ProconBypassMan::Procon::ButtonCollection::BUTTONS_MAP.keys.freeze
|
36
37
|
|
37
38
|
def self.load(button_key)
|
38
39
|
Button.new(button_key)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class ProconBypassMan::Procon
|
2
|
+
class FlipCache
|
3
|
+
def self.fetch(key: , expires_in: , &block)
|
4
|
+
if expires_in.nil?
|
5
|
+
block.call
|
6
|
+
else
|
7
|
+
@@previous_flips_at_table[key] ||= Time.now
|
8
|
+
if @@previous_flips_at_table[key] < Time.now
|
9
|
+
@@previous_flips_at_table[key] = Time.now + expires_in
|
10
|
+
block.call
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# for testing
|
16
|
+
def self.reset!
|
17
|
+
@@previous_flips_at_table = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
reset!
|
21
|
+
end
|
22
|
+
end
|
@@ -13,7 +13,7 @@ module ProconBypassMan::Procon::PushedButtonHelper
|
|
13
13
|
@@compiled = false
|
14
14
|
def compile_if_not_compile_yet!
|
15
15
|
unless @@compiled
|
16
|
-
::ProconBypassMan::Procon::ButtonCollection::BUTTONS_MAP.each do |button,
|
16
|
+
::ProconBypassMan::Procon::ButtonCollection::BUTTONS_MAP.each do |button, _value|
|
17
17
|
define_method "pressed_#{button}?" do
|
18
18
|
pressed_button?(button)
|
19
19
|
end
|
@@ -1,14 +1,16 @@
|
|
1
1
|
class ProconBypassMan::Procon
|
2
|
+
require "procon_bypass_man/procon/data"
|
2
3
|
require "procon_bypass_man/procon/mode_registry"
|
3
4
|
require "procon_bypass_man/procon/macro_registry"
|
4
5
|
require "procon_bypass_man/procon/layer_changeable"
|
5
6
|
require "procon_bypass_man/procon/button_collection"
|
6
7
|
require "procon_bypass_man/procon/pressed_button_helper"
|
7
8
|
require "procon_bypass_man/procon/user_operation"
|
9
|
+
require "procon_bypass_man/procon/flip_cache"
|
8
10
|
|
9
11
|
attr_accessor :user_operation
|
10
12
|
|
11
|
-
def self.
|
13
|
+
def self.reset!
|
12
14
|
@@status = {
|
13
15
|
buttons: {},
|
14
16
|
current_layer_key: :up,
|
@@ -16,7 +18,6 @@ class ProconBypassMan::Procon
|
|
16
18
|
ongoing_mode: ModeRegistry.load(:manual),
|
17
19
|
}
|
18
20
|
end
|
19
|
-
def self.reset!; reset_cvar!; end
|
20
21
|
reset!
|
21
22
|
|
22
23
|
def initialize(binary)
|
@@ -51,15 +52,21 @@ class ProconBypassMan::Procon
|
|
51
52
|
when :manual
|
52
53
|
@@status[:ongoing_mode] = ModeRegistry.load(:manual)
|
53
54
|
current_layer.flip_buttons.each do |button, options|
|
54
|
-
|
55
|
-
|
55
|
+
if !options[:if_pressed]
|
56
|
+
FlipCache.fetch(key: button, expires_in: options[:flip_interval]) do
|
57
|
+
status[button] = !status[button]
|
58
|
+
end
|
56
59
|
next
|
57
60
|
end
|
58
61
|
|
59
62
|
if options[:if_pressed] && options[:if_pressed].all? { |b| user_operation.pressed_button?(b) }
|
60
|
-
|
63
|
+
FlipCache.fetch(key: button, expires_in: options[:flip_interval]) do
|
64
|
+
status[button] = !status[button]
|
65
|
+
end
|
61
66
|
else
|
62
|
-
|
67
|
+
FlipCache.fetch(key: button, expires_in: options[:flip_interval]) do
|
68
|
+
status[button] = false
|
69
|
+
end
|
63
70
|
end
|
64
71
|
end
|
65
72
|
else
|
@@ -99,18 +106,20 @@ class ProconBypassMan::Procon
|
|
99
106
|
if !status[button]
|
100
107
|
user_operation.unpress_button(button)
|
101
108
|
end
|
102
|
-
|
103
|
-
|
104
|
-
user_operation.unpress_button(
|
109
|
+
|
110
|
+
options[:force_neutral]&.each do |force_neutral_button|
|
111
|
+
user_operation.pressed_button?(force_neutral_button) && user_operation.unpress_button(force_neutral_button)
|
105
112
|
end
|
106
113
|
end
|
107
114
|
end
|
108
115
|
|
109
|
-
current_layer.remaps.each do |from_button,
|
116
|
+
current_layer.remaps.each do |from_button, to_buttons|
|
110
117
|
if user_operation.pressed_button?(from_button)
|
111
118
|
user_operation.unpress_button(from_button)
|
112
119
|
# TODO 2重でpressしないようにしたい
|
113
|
-
|
120
|
+
to_buttons[:to].each do |to_button|
|
121
|
+
user_operation.press_button(to_button) unless user_operation.pressed_button?(to_button)
|
122
|
+
end
|
114
123
|
end
|
115
124
|
end
|
116
125
|
|
@@ -3,10 +3,7 @@ require_relative "io_monitor"
|
|
3
3
|
class ProconBypassMan::Runner
|
4
4
|
class InterruptForRestart < StandardError; end
|
5
5
|
|
6
|
-
def initialize
|
7
|
-
@gadget = gadget
|
8
|
-
@procon = procon
|
9
|
-
|
6
|
+
def initialize
|
10
7
|
$will_interval_0_0_0_5 = 0
|
11
8
|
$will_interval_1_6 = 0
|
12
9
|
end
|
@@ -22,7 +19,7 @@ class ProconBypassMan::Runner
|
|
22
19
|
self_write.puts(sig)
|
23
20
|
end
|
24
21
|
rescue ArgumentError
|
25
|
-
ProconBypassMan.logger.
|
22
|
+
ProconBypassMan.logger.error("Signal #{sig} not supported")
|
26
23
|
end
|
27
24
|
end
|
28
25
|
|
@@ -31,7 +28,7 @@ class ProconBypassMan::Runner
|
|
31
28
|
main_loop_pid = fork { main_loop }
|
32
29
|
|
33
30
|
begin
|
34
|
-
while
|
31
|
+
while(readable_io = IO.select([self_read]))
|
35
32
|
signal = readable_io.first[0].gets.strip
|
36
33
|
handle_signal(signal)
|
37
34
|
end
|
@@ -53,6 +50,8 @@ class ProconBypassMan::Runner
|
|
53
50
|
Process.wait
|
54
51
|
@gadget&.close
|
55
52
|
@procon&.close
|
53
|
+
FileUtils.rm_rf(ProconBypassMan.pid_path)
|
54
|
+
FileUtils.rm_rf(ProconBypassMan.digest_path)
|
56
55
|
exit 1
|
57
56
|
end
|
58
57
|
end
|
@@ -97,8 +96,11 @@ class ProconBypassMan::Runner
|
|
97
96
|
loop do
|
98
97
|
break if $will_terminate_token
|
99
98
|
bypass.send_procon_to_gadget!
|
99
|
+
rescue EOFError => e
|
100
|
+
ProconBypassMan.logger.error "Proconと通信ができませんでした.終了処理を開始します"
|
101
|
+
Process.kill "TERM", Process.ppid
|
100
102
|
rescue Errno::EIO, Errno::ENODEV, Errno::EPROTO, IOError => e
|
101
|
-
ProconBypassMan.logger.error "Procon
|
103
|
+
ProconBypassMan.logger.error "Proconが切断されました。終了処理を開始します"
|
102
104
|
Process.kill "TERM", Process.ppid
|
103
105
|
end
|
104
106
|
ProconBypassMan.logger.info "Thread2を終了します"
|
@@ -118,7 +120,7 @@ class ProconBypassMan::Runner
|
|
118
120
|
|
119
121
|
ProconBypassMan.logger.info "子プロセスでgraceful shutdownの準備ができました"
|
120
122
|
begin
|
121
|
-
while
|
123
|
+
while(readable_io = IO.select([self_read]))
|
122
124
|
signal = readable_io.first[0].gets.strip
|
123
125
|
handle_signal(signal)
|
124
126
|
end
|
@@ -132,38 +134,14 @@ class ProconBypassMan::Runner
|
|
132
134
|
end
|
133
135
|
|
134
136
|
def first_negotiation
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
end
|
144
|
-
break if $will_terminate_token
|
145
|
-
rescue IO::EAGAINWaitReadable
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
# ...
|
150
|
-
# switch) 8001
|
151
|
-
# procon) 8101
|
152
|
-
# switch) 8002
|
153
|
-
# が返ってくるプロトコルがあって、これができていないならやり直す
|
154
|
-
loop do
|
155
|
-
begin
|
156
|
-
data = @procon.read_nonblock(128)
|
157
|
-
if data[0] == "\x81".b && data[1] == "\x01".b
|
158
|
-
ProconBypassMan.logger.debug { "接続を確認しました" }
|
159
|
-
@gadget.write_nonblock(data)
|
160
|
-
break
|
161
|
-
else
|
162
|
-
raise ::ProconBypassMan::FirstConnectionError
|
163
|
-
end
|
164
|
-
rescue IO::EAGAINWaitReadable
|
165
|
-
end
|
166
|
-
end
|
137
|
+
return if $will_terminate_token
|
138
|
+
|
139
|
+
@gadget, @procon = ProconBypassMan::DeviceConnector.connect
|
140
|
+
rescue ProconBypassMan::Timer::Timeout
|
141
|
+
::ProconBypassMan.logger.error "デバイスとの通信でタイムアウトが起きて接続ができませんでした。"
|
142
|
+
@gadget&.close
|
143
|
+
@procon&.close
|
144
|
+
raise ::ProconBypassMan::EternalConnectionError
|
167
145
|
end
|
168
146
|
|
169
147
|
def handle_signal(sig)
|
@@ -179,10 +157,14 @@ class ProconBypassMan::Runner
|
|
179
157
|
# @return [void]
|
180
158
|
def print_booted_message
|
181
159
|
booted_message = <<~EOF
|
160
|
+
----
|
161
|
+
RUBY_VERSION: #{RUBY_VERSION}
|
182
162
|
ProconBypassMan: #{ProconBypassMan::VERSION}
|
183
|
-
pid_path: #{ProconBypassMan.pid_path}
|
184
163
|
pid: #{$$}
|
185
|
-
|
164
|
+
root: #{ProconBypassMan.root}
|
165
|
+
pid_path: #{ProconBypassMan.pid_path}
|
166
|
+
setting_path: #{ProconBypassMan::Configuration.instance.setting_path}
|
167
|
+
----
|
186
168
|
EOF
|
187
169
|
ProconBypassMan.logger.info(booted_message)
|
188
170
|
puts booted_message
|
data/lib/procon_bypass_man.rb
CHANGED
@@ -3,11 +3,11 @@ require 'yaml'
|
|
3
3
|
require "fileutils"
|
4
4
|
|
5
5
|
require_relative "procon_bypass_man/version"
|
6
|
-
require_relative "procon_bypass_man/
|
6
|
+
require_relative "procon_bypass_man/timer"
|
7
7
|
require_relative "procon_bypass_man/bypass"
|
8
|
+
require_relative "procon_bypass_man/device_connector"
|
8
9
|
require_relative "procon_bypass_man/runner"
|
9
10
|
require_relative "procon_bypass_man/processor"
|
10
|
-
require_relative "procon_bypass_man/procon/data"
|
11
11
|
require_relative "procon_bypass_man/configuration"
|
12
12
|
require_relative "procon_bypass_man/procon"
|
13
13
|
|
@@ -18,6 +18,7 @@ module ProconBypassMan
|
|
18
18
|
class ProConRejected < StandardError; end
|
19
19
|
class CouldNotLoadConfigError < StandardError; end
|
20
20
|
class FirstConnectionError < StandardError; end
|
21
|
+
class EternalConnectionError < StandardError; end
|
21
22
|
|
22
23
|
def self.configure(setting_path: nil, &block)
|
23
24
|
unless setting_path
|
@@ -33,14 +34,19 @@ module ProconBypassMan
|
|
33
34
|
|
34
35
|
def self.run(setting_path: nil, &block)
|
35
36
|
configure(setting_path: setting_path, &block)
|
36
|
-
at_exit { FileUtils.rm_rf(pid_path) }
|
37
37
|
File.write(pid_path, $$)
|
38
|
-
|
39
|
-
Runner.new(gadget: registry.gadget, procon: registry.procon).run
|
38
|
+
Runner.new.run
|
40
39
|
rescue CouldNotLoadConfigError
|
41
40
|
ProconBypassMan.logger.error "設定ファイルが不正です。設定ファイルの読み込みに失敗しました"
|
42
41
|
puts "設定ファイルが不正です。設定ファイルの読み込みに失敗しました"
|
42
|
+
FileUtils.rm_rf(ProconBypassMan.pid_path)
|
43
|
+
FileUtils.rm_rf(ProconBypassMan.digest_path)
|
43
44
|
exit 1
|
45
|
+
rescue EternalConnectionError
|
46
|
+
ProconBypassMan.logger.error "接続の見込みがないのでsleepしまくります"
|
47
|
+
puts "接続の見込みがないのでsleepしまくります"
|
48
|
+
FileUtils.rm_rf(ProconBypassMan.pid_path)
|
49
|
+
sleep(999999999)
|
44
50
|
rescue FirstConnectionError
|
45
51
|
puts "接続を確立できませんでした。やりなおします。"
|
46
52
|
retry
|
@@ -58,9 +64,8 @@ module ProconBypassMan
|
|
58
64
|
end
|
59
65
|
end
|
60
66
|
|
61
|
-
DEFAULT_PID_PATH = File.expand_path('../pbm_pid', __dir__).freeze
|
62
67
|
def self.pid_path
|
63
|
-
@@pid_path ||=
|
68
|
+
@@pid_path ||= File.expand_path("#{root}/pbm_pid", __dir__).freeze
|
64
69
|
end
|
65
70
|
|
66
71
|
def self.reset!
|
@@ -72,6 +77,18 @@ module ProconBypassMan
|
|
72
77
|
end
|
73
78
|
|
74
79
|
def self.root
|
75
|
-
|
80
|
+
if defined?(@@root)
|
81
|
+
@@root
|
82
|
+
else
|
83
|
+
File.expand_path('..', __dir__).freeze
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.root=(path)
|
88
|
+
@@root = path
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.digest_path
|
92
|
+
"#{root}/.setting_yaml_digest"
|
76
93
|
end
|
77
94
|
end
|
data/procon_bypass_man.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
|
11
11
|
spec.summary = "extension for Nintendo Switch Pro Controller"
|
12
12
|
spec.description = spec.summary
|
13
|
-
spec.homepage = "https://github.com/
|
13
|
+
spec.homepage = "https://github.com/splaplapla/procon_bypass_man"
|
14
14
|
spec.license = "MIT"
|
15
15
|
spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
|
16
16
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Project Template
|
2
|
+
https://github.com/splaplapla/pbmenv で使っているファイルです
|
3
|
+
|
4
|
+
## systemd
|
5
|
+
* sudo ln -s /usr/share/pbm/current/systemd_units/pbm.service /etc/systemd/system/pbm.service
|
6
|
+
* commnds
|
7
|
+
* systemctl daemon-reload
|
8
|
+
* systemctl enable pbm.service
|
9
|
+
* systemctl disable pbm.service
|
10
|
+
* systemctl start pbm.service
|
11
|
+
* systemctl status pbm.service
|
12
|
+
* systemctl restart pbm.service
|
13
|
+
|
14
|
+
### ログ
|
15
|
+
* journalctl -xe -f
|
16
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/inline'
|
4
|
+
|
5
|
+
gemfile do
|
6
|
+
source 'https://rubygems.org'
|
7
|
+
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
8
|
+
gem 'procon_bypass_man', '0.1.7'
|
9
|
+
gem 'procon_bypass_man-splatoon2', github: 'splaplapla/procon_bypass_man-splatoon2', tag: "0.1.1"
|
10
|
+
end
|
11
|
+
|
12
|
+
ProconBypassMan.tap do |pbm|
|
13
|
+
pbm.root = File.expand_path(__dir__)
|
14
|
+
pbm.logger = Logger.new("#{ProconBypassMan.root}/app.log", 5, 1024 * 1024 * 10)
|
15
|
+
pbm.logger.level = :debug
|
16
|
+
end
|
17
|
+
|
18
|
+
ProconBypassMan.run(setting_path: "./setting.yml")
|
@@ -0,0 +1,35 @@
|
|
1
|
+
version: 1.0
|
2
|
+
setting: |-
|
3
|
+
fast_return = ProconBypassMan::Splatoon2::Macro::FastReturn
|
4
|
+
guruguru = ProconBypassMan::Splatoon2::Mode::Guruguru
|
5
|
+
|
6
|
+
install_macro_plugin fast_return
|
7
|
+
install_macro_plugin ProconBypassMan::Splatoon2::Macro::JumpToUpKey
|
8
|
+
install_macro_plugin ProconBypassMan::Splatoon2::Macro::JumpToRightKey
|
9
|
+
install_macro_plugin ProconBypassMan::Splatoon2::Macro::JumpToLeftKey
|
10
|
+
install_mode_plugin guruguru
|
11
|
+
|
12
|
+
prefix_keys_for_changing_layer [:zr, :zl, :l]
|
13
|
+
|
14
|
+
layer :up, mode: :manual do
|
15
|
+
# flip :zr, if_pressed: :zr, force_neutral: :zl
|
16
|
+
flip :zr, if_pressed: :zr, force_neutral: :zl
|
17
|
+
flip :zl, if_pressed: [:y, :b, :zl]
|
18
|
+
flip :a, if_pressed: [:a]
|
19
|
+
flip :down, if_pressed: :down
|
20
|
+
macro fast_return.name, if_pressed: [:y, :b, :down]
|
21
|
+
macro ProconBypassMan::Splatoon2::Macro::JumpToUpKey, if_pressed: [:y, :b, :up]
|
22
|
+
macro ProconBypassMan::Splatoon2::Macro::JumpToRightKey, if_pressed: [:y, :b, :right]
|
23
|
+
macro ProconBypassMan::Splatoon2::Macro::JumpToLeftKey, if_pressed: [:y, :b, :left]
|
24
|
+
remap :l, to: :zr
|
25
|
+
end
|
26
|
+
layer :right, mode: guruguru.name
|
27
|
+
layer :left do
|
28
|
+
# flip :zr, if_pressed: :zr, force_neutral: :zl
|
29
|
+
remap :l, to: :zr
|
30
|
+
end
|
31
|
+
layer :down do
|
32
|
+
# flip :zl
|
33
|
+
# flip :zr, if_pressed: :zr, force_neutral: :zl, flip_interval: "1F"
|
34
|
+
remap :l, to: :zr
|
35
|
+
end
|