procon_bypass_man 0.1.5 → 0.1.9
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 +41 -13
- data/.github/workflows/ruby.yml +34 -0
- data/.rubocop.yml +24 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +15 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +75 -3
- data/README.md +22 -8
- data/Rakefile +10 -1
- data/Steepfile +39 -0
- data/bin/report_receive_server.rb +11 -0
- data/docs/setup_raspi.md +12 -7
- data/docs/setup_raspi.mitamae.rb +60 -0
- data/docs/setup_raspi_by_mitamae.md +14 -0
- data/examples/practical/app.rb +2 -2
- data/lib/procon_bypass_man/analog_stick_position.rb +14 -0
- data/lib/procon_bypass_man/boot_message.rb +40 -0
- data/lib/procon_bypass_man/bypass.rb +20 -7
- data/lib/procon_bypass_man/configuration/layer.rb +33 -3
- data/lib/procon_bypass_man/configuration/loader.rb +17 -16
- data/lib/procon_bypass_man/configuration/validator.rb +20 -19
- data/lib/procon_bypass_man/configuration.rb +24 -12
- data/lib/procon_bypass_man/device_connector.rb +14 -31
- data/lib/procon_bypass_man/error_reporter.rb +44 -0
- data/lib/procon_bypass_man/io_monitor.rb +7 -4
- data/lib/procon_bypass_man/on_memory_cache.rb +34 -0
- data/lib/procon_bypass_man/procon/analog_stick_cap.rb +88 -0
- data/lib/procon_bypass_man/procon/button_collection.rb +14 -6
- data/lib/procon_bypass_man/procon/debug_dumper.rb +17 -0
- data/lib/procon_bypass_man/procon/mode_registry.rb +2 -2
- data/lib/procon_bypass_man/procon/press_button_aware.rb +13 -0
- data/lib/procon_bypass_man/procon/pressed_button_helper.rb +1 -11
- data/lib/procon_bypass_man/procon/user_operation.rb +10 -3
- data/lib/procon_bypass_man/procon.rb +15 -1
- data/lib/procon_bypass_man/reporter.rb +42 -0
- data/lib/procon_bypass_man/runner.rb +39 -50
- data/lib/procon_bypass_man/uptime.rb +13 -0
- data/lib/procon_bypass_man/version.rb +1 -1
- data/lib/procon_bypass_man.rb +53 -2
- data/procon_bypass_man.gemspec +1 -1
- data/project_template/README.md +17 -0
- data/project_template/app.rb +20 -0
- data/project_template/setting.yml +35 -0
- data/project_template/systemd_units/pbm.service +13 -0
- data/sig/README.rb +4 -0
- data/sig/main.rbs +467 -0
- metadata +27 -8
- data/examples/pbm.service +0 -27
- data/examples/simple.rb +0 -13
@@ -1,13 +1,14 @@
|
|
1
1
|
module ProconBypassMan
|
2
2
|
class Configuration
|
3
3
|
class Layer
|
4
|
-
attr_accessor :mode, :flips, :macros, :remaps
|
4
|
+
attr_accessor :mode, :flips, :macros, :remaps, :left_analog_stick_caps
|
5
5
|
|
6
|
-
def initialize(mode: :manual
|
6
|
+
def initialize(mode: :manual)
|
7
7
|
self.mode = mode
|
8
8
|
self.flips = {}
|
9
9
|
self.macros = {}
|
10
10
|
self.remaps = {}
|
11
|
+
self.left_analog_stick_caps = {}
|
11
12
|
instance_eval(&block) if block_given?
|
12
13
|
end
|
13
14
|
|
@@ -50,7 +51,6 @@ module ProconBypassMan
|
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
53
|
-
PRESET_MACROS = [:fast_return]
|
54
54
|
def macro(name, if_pressed: )
|
55
55
|
if name.respond_to?(:name)
|
56
56
|
macro_name = name.name.to_sym
|
@@ -72,6 +72,36 @@ module ProconBypassMan
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
+
def left_analog_stick_cap(cap: , if_pressed: nil, force_neutral: nil)
|
76
|
+
hash = { cap: cap }
|
77
|
+
|
78
|
+
case if_pressed
|
79
|
+
when TrueClass
|
80
|
+
raise "not support class"
|
81
|
+
when Symbol, String
|
82
|
+
if_pressed = [if_pressed]
|
83
|
+
when Array, FalseClass
|
84
|
+
# sono mama
|
85
|
+
when NilClass
|
86
|
+
if_pressed = nil
|
87
|
+
else
|
88
|
+
raise "not support if_pressed"
|
89
|
+
end
|
90
|
+
|
91
|
+
if force_neutral
|
92
|
+
case force_neutral
|
93
|
+
when TrueClass, FalseClass
|
94
|
+
raise "ボタンを渡してください"
|
95
|
+
when Symbol, String
|
96
|
+
hash[:force_neutral] = [force_neutral]
|
97
|
+
when Array
|
98
|
+
hash[:force_neutral] = force_neutral
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
left_analog_stick_caps[if_pressed] = hash
|
103
|
+
end
|
104
|
+
|
75
105
|
# @return [Array]
|
76
106
|
def flip_buttons
|
77
107
|
flips
|
@@ -1,24 +1,22 @@
|
|
1
1
|
module ProconBypassMan
|
2
2
|
class Configuration
|
3
3
|
module Loader
|
4
|
+
require 'digest/md5'
|
5
|
+
|
4
6
|
def self.load(setting_path: )
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
instance.errors[:base] << "yamlのシンタックスエラーです"
|
14
|
-
next(instance)
|
7
|
+
ProconBypassMan::Configuration.switch_new_context(:validation) do |validation_instance|
|
8
|
+
yaml = YAML.load_file(setting_path) or raise "読み込みに失敗しました"
|
9
|
+
validation_instance.instance_eval(yaml["setting"])
|
10
|
+
validator = Validator.new(validation_instance)
|
11
|
+
if validator.valid?
|
12
|
+
next
|
13
|
+
else
|
14
|
+
raise ProconBypassMan::CouldNotLoadConfigError, validator.errors
|
15
15
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
if !validation_instance.errors.empty?
|
21
|
-
raise ProconBypassMan::CouldNotLoadConfigError, validation_instance.errors
|
16
|
+
rescue SyntaxError
|
17
|
+
raise ProconBypassMan::CouldNotLoadConfigError, "Rubyのシンタックスエラーです"
|
18
|
+
rescue Psych::SyntaxError
|
19
|
+
raise ProconBypassMan::CouldNotLoadConfigError, "yamlのシンタックスエラーです"
|
22
20
|
end
|
23
21
|
|
24
22
|
yaml = YAML.load_file(setting_path)
|
@@ -33,6 +31,9 @@ module ProconBypassMan
|
|
33
31
|
ProconBypassMan.logger.warn "不明なバージョンです。failoverします"
|
34
32
|
ProconBypassMan::Configuration.instance.instance_eval(yaml["setting"])
|
35
33
|
end
|
34
|
+
|
35
|
+
File.write(ProconBypassMan.digest_path, Digest::MD5.hexdigest(yaml["setting"]))
|
36
|
+
|
36
37
|
ProconBypassMan::Configuration.instance
|
37
38
|
end
|
38
39
|
|
@@ -1,6 +1,11 @@
|
|
1
1
|
module ProconBypassMan
|
2
2
|
class Configuration
|
3
|
-
|
3
|
+
class Validator
|
4
|
+
def initialize(config)
|
5
|
+
@layers = config.layers
|
6
|
+
@prefix_keys = config.prefix_keys
|
7
|
+
end
|
8
|
+
|
4
9
|
# @return [Boolean]
|
5
10
|
def valid?
|
6
11
|
@errors = Hash.new {|h,k| h[k] = [] }
|
@@ -38,7 +43,7 @@ module ProconBypassMan
|
|
38
43
|
end
|
39
44
|
|
40
45
|
def validate_require_prefix_keys
|
41
|
-
if prefix_keys.empty?
|
46
|
+
if @prefix_keys.empty?
|
42
47
|
@errors[:prefix_keys] ||= []
|
43
48
|
@errors[:prefix_keys] << "prefix_keys_for_changing_layerに値が入っていません。"
|
44
49
|
end
|
@@ -48,24 +53,20 @@ module ProconBypassMan
|
|
48
53
|
@layers.each do |layer_key, value|
|
49
54
|
unverified_buttons = []
|
50
55
|
# teplevel target button
|
51
|
-
value.
|
52
|
-
value.
|
56
|
+
value.flips.keys.map(&:to_sym).each { |b| unverified_buttons << b }
|
57
|
+
value.remaps.keys.map(&:to_sym).each { |b| unverified_buttons << b }
|
53
58
|
# internal target button
|
54
|
-
value.
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
flip_option_target_button
|
60
|
-
}
|
59
|
+
value.flips.flat_map { |_flip_button, flip_option|
|
60
|
+
flip_option.flat_map { |flip_option_key, flip_option_target_button|
|
61
|
+
next if flip_option_key == :flip_interval
|
62
|
+
next if flip_option_target_button.is_a?(FalseClass) || flip_option_target_button.is_a?(TrueClass)
|
63
|
+
flip_option_target_button
|
61
64
|
}
|
62
65
|
}.compact.each { |b| unverified_buttons << b }
|
63
|
-
value.
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
flip_option_target_button
|
68
|
-
}
|
66
|
+
value.remaps.flat_map { |_button, option|
|
67
|
+
option.flat_map { |_flip_option_key, flip_option_target_button|
|
68
|
+
next if flip_option_target_button.is_a?(FalseClass) || flip_option_target_button.is_a?(TrueClass)
|
69
|
+
flip_option_target_button
|
69
70
|
}
|
70
71
|
}.compact.each { |b| unverified_buttons << b }
|
71
72
|
unless(nunsupport_buttons = (unverified_buttons - ProconBypassMan::Procon::ButtonCollection::BUTTONS)).length.zero?
|
@@ -78,8 +79,8 @@ module ProconBypassMan
|
|
78
79
|
@layers.each do |layer_key, value|
|
79
80
|
flip_buttons = []
|
80
81
|
remap_buttons = []
|
81
|
-
value.
|
82
|
-
value.
|
82
|
+
value.flips.keys.map(&:to_sym).each { |b| flip_buttons << b }
|
83
|
+
value.remaps.keys.map(&:to_sym).each { |b| remap_buttons << b }
|
83
84
|
if(duplicated_buttons = flip_buttons & remap_buttons).length > 0
|
84
85
|
@errors[:layers] << "レイヤー#{layer_key}で、連打とリマップの定義が重複しているボタン#{duplicated_buttons.join(", ")}があります"
|
85
86
|
end
|
@@ -4,14 +4,13 @@ require "procon_bypass_man/configuration/layer"
|
|
4
4
|
|
5
5
|
module ProconBypassMan
|
6
6
|
class Configuration
|
7
|
-
include Validator
|
8
|
-
|
9
7
|
attr_accessor :layers,
|
10
8
|
:setting_path,
|
11
9
|
:mode_plugins,
|
12
10
|
:macro_plugins,
|
13
11
|
:context,
|
14
|
-
:current_context_key
|
12
|
+
:current_context_key,
|
13
|
+
:neutral_position
|
15
14
|
|
16
15
|
def self.instance
|
17
16
|
@@current_context_key ||= :main
|
@@ -19,13 +18,12 @@ module ProconBypassMan
|
|
19
18
|
@@context[@@current_context_key] ||= new
|
20
19
|
end
|
21
20
|
|
22
|
-
def self.
|
23
|
-
@@context[key]
|
21
|
+
def self.switch_new_context(key)
|
22
|
+
@@context[key] = new
|
24
23
|
previous_key = @@current_context_key
|
25
24
|
if block_given?
|
26
25
|
@@current_context_key = key
|
27
26
|
value = yield(@@context[key])
|
28
|
-
@@context[key].reset!
|
29
27
|
@@current_context_key = previous_key
|
30
28
|
return value
|
31
29
|
else
|
@@ -37,13 +35,21 @@ module ProconBypassMan
|
|
37
35
|
reset!
|
38
36
|
end
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
mode_name = mode.name.to_sym
|
44
|
-
else
|
45
|
-
mode_name = mode
|
38
|
+
module ManualMode
|
39
|
+
def self.name
|
40
|
+
'manual'
|
46
41
|
end
|
42
|
+
end
|
43
|
+
MODES = [:manual]
|
44
|
+
def layer(direction, mode: ManualMode, &block)
|
45
|
+
mode_name = case mode
|
46
|
+
when String
|
47
|
+
mode.to_sym
|
48
|
+
when Symbol
|
49
|
+
mode
|
50
|
+
else
|
51
|
+
mode.name.to_sym
|
52
|
+
end
|
47
53
|
unless (MODES + ProconBypassMan::Procon::ModeRegistry.plugins.keys).include?(mode_name)
|
48
54
|
raise("#{mode_name} mode is unknown")
|
49
55
|
end
|
@@ -69,6 +75,11 @@ module ProconBypassMan
|
|
69
75
|
self
|
70
76
|
end
|
71
77
|
|
78
|
+
def set_neutral_position(x, y)
|
79
|
+
self.neutral_position = AnalogStickPosition.new(x: x, y: y)
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
72
83
|
def prefix_keys
|
73
84
|
@prefix_keys_for_changing_layer
|
74
85
|
end
|
@@ -83,6 +94,7 @@ module ProconBypassMan
|
|
83
94
|
left: Layer.new,
|
84
95
|
right: Layer.new,
|
85
96
|
}
|
97
|
+
@neutral_position = AnalogStickPosition.new(x: 2124, y: 1808)
|
86
98
|
end
|
87
99
|
end
|
88
100
|
end
|
@@ -12,24 +12,7 @@ class ProconBypassMan::DeviceConnector
|
|
12
12
|
PROCON_PATH = "/dev/hidraw0"
|
13
13
|
PROCON2_PATH = "/dev/hidraw1"
|
14
14
|
|
15
|
-
|
16
|
-
def self.reset_connection!
|
17
|
-
s = new
|
18
|
-
s.add([
|
19
|
-
["0000"],
|
20
|
-
["0000"],
|
21
|
-
["8005"],
|
22
|
-
["0000"],
|
23
|
-
["8001"],
|
24
|
-
], read_from: :switch)
|
25
|
-
s.drain_all
|
26
|
-
s.read_procon
|
27
|
-
s.write_switch("213c910080005db7723d48720a800300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
28
|
-
sleep(10) # いらないかも
|
29
|
-
s
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.connect(throw_error_if_timeout: true, enable_at_exit: false)
|
15
|
+
def self.connect
|
33
16
|
s = new(throw_error_if_timeout: true, enable_at_exit: false)
|
34
17
|
s.add([
|
35
18
|
["0000"],
|
@@ -37,16 +20,16 @@ class ProconBypassMan::DeviceConnector
|
|
37
20
|
["8005"],
|
38
21
|
["0010"],
|
39
22
|
], read_from: :switch)
|
40
|
-
# 1
|
23
|
+
# 1. Sends current connection status, and if the Joy-Con are connected,
|
41
24
|
s.add([["8001"]], read_from: :switch)
|
42
25
|
s.add([/^8101/], read_from: :procon) # <<< 81010003176d96e7a5480000000, macaddressとコントローラー番号を返す
|
43
|
-
# 2
|
26
|
+
# 2. Sends handshaking packets over UART to the Joy-Con or Pro Controller Broadcom chip. This command can only be called once per session.
|
44
27
|
s.add([["8002"]], read_from: :switch)
|
45
28
|
s.add([/^8102/], read_from: :procon)
|
46
29
|
# 3
|
47
30
|
s.add([/^0100/], read_from: :switch)
|
48
31
|
s.add([/^21/], read_from: :procon)
|
49
|
-
# 4
|
32
|
+
# 4. Forces the Joy-Con or Pro Controller to only talk over USB HID without any timeouts. This is required for the Pro Controller to not time out and revert to Bluetooth.
|
50
33
|
s.add([["8004"]], read_from: :switch)
|
51
34
|
s.drain_all
|
52
35
|
return [s.switch, s.procon]
|
@@ -75,7 +58,7 @@ class ProconBypassMan::DeviceConnector
|
|
75
58
|
timer = ProconBypassMan::Timer.new
|
76
59
|
begin
|
77
60
|
timer.throw_if_timeout!
|
78
|
-
data = from_device(item).read_nonblock(
|
61
|
+
data = from_device(item).read_nonblock(64)
|
79
62
|
rescue IO::EAGAINWaitReadable
|
80
63
|
retry
|
81
64
|
end
|
@@ -128,7 +111,7 @@ class ProconBypassMan::DeviceConnector
|
|
128
111
|
timer = ProconBypassMan::Timer.new
|
129
112
|
begin
|
130
113
|
timer.throw_if_timeout!
|
131
|
-
data = switch.read_nonblock(
|
114
|
+
data = switch.read_nonblock(64)
|
132
115
|
ProconBypassMan.logger.debug { " >>> #{data.unpack("H*")})" }
|
133
116
|
rescue IO::EAGAINWaitReadable
|
134
117
|
retry
|
@@ -163,7 +146,7 @@ class ProconBypassMan::DeviceConnector
|
|
163
146
|
timer = ProconBypassMan::Timer.new
|
164
147
|
begin
|
165
148
|
timer.throw_if_timeout!
|
166
|
-
data = procon.read_nonblock(
|
149
|
+
data = procon.read_nonblock(64)
|
167
150
|
ProconBypassMan.logger.error " <<< #{data.unpack("H*")})"
|
168
151
|
rescue IO::EAGAINWaitReadable
|
169
152
|
retry
|
@@ -184,7 +167,7 @@ class ProconBypassMan::DeviceConnector
|
|
184
167
|
timer = ProconBypassMan::Timer.new
|
185
168
|
begin
|
186
169
|
timer.throw_if_timeout!
|
187
|
-
data = procon.read_nonblock(
|
170
|
+
data = procon.read_nonblock(64)
|
188
171
|
ProconBypassMan.logger.debug { " <<< #{data.unpack("H*")})" }
|
189
172
|
rescue IO::EAGAINWaitReadable
|
190
173
|
retry
|
@@ -217,7 +200,7 @@ class ProconBypassMan::DeviceConnector
|
|
217
200
|
timer = ProconBypassMan::Timer.new
|
218
201
|
begin
|
219
202
|
timer.throw_if_timeout!
|
220
|
-
data = switch.read_nonblock(
|
203
|
+
data = switch.read_nonblock(64)
|
221
204
|
ProconBypassMan.logger.debug { " >>> #{data.unpack("H*")})" }
|
222
205
|
rescue IO::EAGAINWaitReadable
|
223
206
|
retry
|
@@ -281,7 +264,7 @@ class ProconBypassMan::DeviceConnector
|
|
281
264
|
|
282
265
|
file = File.open(path, "w+")
|
283
266
|
begin
|
284
|
-
file.read_nonblock(
|
267
|
+
file.read_nonblock(64)
|
285
268
|
rescue EOFError
|
286
269
|
file.close
|
287
270
|
return false
|
@@ -303,12 +286,12 @@ class ProconBypassMan::DeviceConnector
|
|
303
286
|
case
|
304
287
|
when is_available_device?(PROCON_PATH)
|
305
288
|
ProconBypassMan.logger.info "proconのデバイスファイルは#{PROCON_PATH}を使います"
|
306
|
-
@procon = File.open(PROCON_PATH, "w+")
|
307
|
-
@gadget = File.open('/dev/hidg0', "w+")
|
289
|
+
@procon = File.open(PROCON_PATH, "w+b")
|
290
|
+
@gadget = File.open('/dev/hidg0', "w+b")
|
308
291
|
when is_available_device?(PROCON2_PATH)
|
309
292
|
ProconBypassMan.logger.info "proconのデバイスファイルは#{PROCON2_PATH}を使います"
|
310
|
-
@procon = File.open(PROCON2_PATH, "w+")
|
311
|
-
@gadget = File.open('/dev/hidg0', "w+")
|
293
|
+
@procon = File.open(PROCON2_PATH, "w+b")
|
294
|
+
@gadget = File.open('/dev/hidg0', "w+b")
|
312
295
|
else
|
313
296
|
raise "/dev/hidraw0, /dev/hidraw1の両方見つかりませんでした"
|
314
297
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
class ProconBypassMan::ErrorReporter
|
5
|
+
PATH = "/api/error_reports" # POST
|
6
|
+
|
7
|
+
class Client
|
8
|
+
def initialize
|
9
|
+
@server = ProconBypassMan.api_server
|
10
|
+
@hostname = `hostname`.chomp
|
11
|
+
end
|
12
|
+
|
13
|
+
def post(body: )
|
14
|
+
# TODO ここでvalidationする
|
15
|
+
if @server.nil?
|
16
|
+
ProconBypassMan.logger.info('送信先が未設定なのでスキップしました')
|
17
|
+
return
|
18
|
+
end
|
19
|
+
|
20
|
+
uri = URI.parse("#{@server}#{PATH}")
|
21
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
22
|
+
http.use_ssl = uri.scheme === "https"
|
23
|
+
response = http.post(
|
24
|
+
uri.path,
|
25
|
+
{ report: body.full_message.to_json, hostname: @hostname }.to_json,
|
26
|
+
{ "Content-Type" => "application/json" },
|
27
|
+
)
|
28
|
+
unless response.code == /^20/
|
29
|
+
ProconBypassMan.logger.error(response.body)
|
30
|
+
end
|
31
|
+
rescue => e
|
32
|
+
puts e
|
33
|
+
ProconBypassMan.logger.error(e)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.report(body: )
|
38
|
+
ProconBypassMan.logger.error(body)
|
39
|
+
Client.new.post(body: body)
|
40
|
+
rescue => e
|
41
|
+
ProconBypassMan.logger.error(e)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -62,10 +62,13 @@ module ProconBypassMan
|
|
62
62
|
}.join(", ")
|
63
63
|
max_output_length = line.length
|
64
64
|
sleep 0.7
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
|
66
|
+
if ENV["PBM_FOREGROUND"]
|
67
|
+
print "\r"
|
68
|
+
print " " * max_output_length
|
69
|
+
print "\r"
|
70
|
+
print line
|
71
|
+
end
|
69
72
|
ProconBypassMan.logger.debug { line }
|
70
73
|
break if $will_terminate_token
|
71
74
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class ProconBypassMan::OnMemoryCache
|
2
|
+
class CacheValue
|
3
|
+
attr_accessor :expired_at, :value
|
4
|
+
|
5
|
+
def initialize(expired_at: , value: )
|
6
|
+
self.expired_at = expired_at
|
7
|
+
self.value = value
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@table = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Integer] expires_in 秒数
|
16
|
+
# @param [String] key
|
17
|
+
def fetch(key: , expires_in: , &block)
|
18
|
+
now = Time.now
|
19
|
+
if @table[key].nil?
|
20
|
+
value = block.call
|
21
|
+
value_object = CacheValue.new(expired_at: now + expires_in, value: value)
|
22
|
+
@table[key] = value_object
|
23
|
+
return value
|
24
|
+
end
|
25
|
+
|
26
|
+
if @table[key].expired_at < now
|
27
|
+
value = block.call
|
28
|
+
@table[key] = CacheValue.new(expired_at: now + expires_in, value: value)
|
29
|
+
return value
|
30
|
+
else
|
31
|
+
return @table[key].value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
class ProconBypassMan::Procon::AnalogStickCap
|
2
|
+
class Position
|
3
|
+
attr_accessor :x, :y
|
4
|
+
|
5
|
+
def initialize(x:, y:)
|
6
|
+
@x = x.to_i
|
7
|
+
@y = y.to_i
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_binary
|
11
|
+
analog_stick_data = [
|
12
|
+
(@x & "0xff".to_i(16)),
|
13
|
+
((@y << 4) & "0xf0".to_i(16)) | ((@x >> 8) & "0x0f".to_i(16)),
|
14
|
+
(@y >> 4) & "0xff".to_i(16),
|
15
|
+
]
|
16
|
+
hex = analog_stick_data.map{ |x| x.to_s(16).rjust(2, "0") }.join
|
17
|
+
[hex].pack("H*")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :bin_x, :bin_y
|
22
|
+
attr_accessor :neutral_position
|
23
|
+
|
24
|
+
def initialize(binary)
|
25
|
+
@neutral_position = ProconBypassMan::Configuration.instance.neutral_position
|
26
|
+
@binary = binary
|
27
|
+
|
28
|
+
byte6 = binary[6].unpack("H*").first.to_i(16).to_s(2).rjust(8, "0")
|
29
|
+
byte7 = binary[7].unpack("H*").first.to_i(16).to_s(2).rjust(8, "0")
|
30
|
+
byte8 = binary[8].unpack("H*").first.to_i(16).to_s(2).rjust(8, "0")
|
31
|
+
|
32
|
+
self.bin_x = "#{byte7[4..7]}#{byte6}"
|
33
|
+
self.bin_y = "#{byte8}#{byte7[0..3]}"
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [ProconBypassMan::Procon::AnalogStickCap::Position]
|
37
|
+
def capped_position(cap_hypotenuse: )
|
38
|
+
if hypotenuse > cap_hypotenuse
|
39
|
+
relative_capped_x = cap_hypotenuse * Math.cos(rad * Math::PI / 180).abs
|
40
|
+
relative_capped_y = cap_hypotenuse * Math.sin(rad * Math::PI / 180).abs
|
41
|
+
relative_capped_x = -(relative_capped_x.abs) if relative_x.negative?
|
42
|
+
relative_capped_y = -(relative_capped_y.abs) if relative_y.negative?
|
43
|
+
return Position.new(
|
44
|
+
x: relative_capped_x + neutral_position.x,
|
45
|
+
y: relative_capped_y + neutral_position.y,
|
46
|
+
)
|
47
|
+
else
|
48
|
+
return position
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [ProconBypassMan::Procon::AnalogStickCap::Position]
|
53
|
+
def position
|
54
|
+
Position.new(x: abs_x, y: abs_y)
|
55
|
+
end
|
56
|
+
|
57
|
+
# 0, 0からのx
|
58
|
+
def abs_x
|
59
|
+
bin_x.to_i(2)
|
60
|
+
end
|
61
|
+
|
62
|
+
# 0, 0からのy
|
63
|
+
def abs_y
|
64
|
+
bin_y.to_i(2)
|
65
|
+
end
|
66
|
+
|
67
|
+
def relative_x
|
68
|
+
bin_x.to_i(2) - neutral_position.x
|
69
|
+
end
|
70
|
+
|
71
|
+
def relative_y
|
72
|
+
bin_y.to_i(2) - neutral_position.y
|
73
|
+
end
|
74
|
+
|
75
|
+
# @deprecated
|
76
|
+
def x; relative_x; end
|
77
|
+
def y; relative_y; end
|
78
|
+
|
79
|
+
def rad
|
80
|
+
(
|
81
|
+
Math.atan(y / x.to_f) * 180 / Math::PI
|
82
|
+
).floor(6)
|
83
|
+
end
|
84
|
+
|
85
|
+
def hypotenuse
|
86
|
+
Math.sqrt(x**2 + y**2).floor(6)
|
87
|
+
end
|
88
|
+
end
|
@@ -8,15 +8,20 @@ class ProconBypassMan::Procon::ButtonCollection
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
+
# https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/ac8093c84194b3232acb675ac1accce9bcb456a3/bluetooth_hid_notes.md
|
12
|
+
#0) Input report ID
|
13
|
+
#1) Timer. Increments very fast. Can be used to estimate excess Bluetooth latency.
|
14
|
+
#2 high nibble) Battery level. 8=full, 6=medium, 4=low, 2=critical, 0=empty. LSB=Charging.
|
15
|
+
#2 low nibble) Connection info. (con_info >> 1) & 3 - 3=JC, 0=Pro/ChrGrip. con_info & 1 - 1=Switch/USB powered.
|
11
16
|
#3) ZR R SR(right) SL(right) A B X Y
|
12
17
|
#4) Grip (none) Cap Home ThumbL ThumbR + -
|
13
18
|
#5) ZL L SL(left) SR(left) Left Right Up Down
|
14
|
-
#6) analog[0]
|
15
|
-
#7) analog[1]
|
16
|
-
#8) analog[2]
|
17
|
-
#9) analog[3]
|
18
|
-
#a) analog[4]
|
19
|
-
#b) analog[5]
|
19
|
+
#6) analog[0] Left analog stick data
|
20
|
+
#7) analog[1] Left analog stick data
|
21
|
+
#8) analog[2] Left analog stick data
|
22
|
+
#9) analog[3] Right analog stick data
|
23
|
+
#a) analog[4] Right analog stick data
|
24
|
+
#b) analog[5] Right analog stick data
|
20
25
|
BYTES_MAP = {
|
21
26
|
0 => nil,
|
22
27
|
1 => nil,
|
@@ -24,6 +29,9 @@ class ProconBypassMan::Procon::ButtonCollection
|
|
24
29
|
3 => [:zr, :r, :sr, :sl, :a, :b, :x, :y],
|
25
30
|
4 => [:grip, :_undefined_key, :cap, :home, :thumbl, :thumbr, :plus, :minus],
|
26
31
|
5 => [:zl, :l, :sl, :sr, :left, :right, :up, :down],
|
32
|
+
6 => [],
|
33
|
+
7 => [],
|
34
|
+
8 => [],
|
27
35
|
}.freeze
|
28
36
|
|
29
37
|
BUTTONS_MAP = BYTES_MAP.reduce({}) { |acc, value|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class ProconBypassMan::Procon::DebugDumper
|
2
|
+
def initialize(binary: )
|
3
|
+
@binary = binary
|
4
|
+
# ProconBypassMan.logger.debug { "<<< patched #{@binary.unpack("H*")}" }
|
5
|
+
end
|
6
|
+
|
7
|
+
def dump_analog_sticks
|
8
|
+
byte6 = @binary[6].unpack("H*").first.to_i(16).to_s(2).rjust(8, "0")
|
9
|
+
byte7 = @binary[7].unpack("H*").first.to_i(16).to_s(2).rjust(8, "0")
|
10
|
+
byte8 = @binary[8].unpack("H*").first.to_i(16).to_s(2).rjust(8, "0")
|
11
|
+
|
12
|
+
x = "#{byte7[4..7]}#{byte6}"
|
13
|
+
y = "#{byte8}#{byte7[0..3]}"
|
14
|
+
ProconBypassMan.logger.debug "x: #{x}, val: #{x.to_i(2)}"
|
15
|
+
ProconBypassMan.logger.debug "y: #{y}, val: #{y.to_i(2)}"
|
16
|
+
end
|
17
|
+
end
|
@@ -23,10 +23,10 @@ class ProconBypassMan::Procon::ModeRegistry
|
|
23
23
|
}
|
24
24
|
|
25
25
|
def self.install_plugin(klass)
|
26
|
-
if plugins[klass.name]
|
26
|
+
if plugins[klass.name.to_sym]
|
27
27
|
raise "すでに登録済みです"
|
28
28
|
end
|
29
|
-
plugins[klass.name] = klass.binaries
|
29
|
+
plugins[klass.name.to_sym] = klass.binaries
|
30
30
|
end
|
31
31
|
|
32
32
|
def self.load(name)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class ProconBypassMan::PpressButtonAware
|
2
|
+
def initialize(binary)
|
3
|
+
@binary = binary
|
4
|
+
end
|
5
|
+
|
6
|
+
def pressed_button?(button)
|
7
|
+
@binary[
|
8
|
+
::ProconBypassMan::Procon::ButtonCollection.load(button).byte_position
|
9
|
+
].unpack("H*").first.to_i(16).to_s(2).reverse[
|
10
|
+
::ProconBypassMan::Procon::ButtonCollection.load(button).bit_position
|
11
|
+
] == '1'
|
12
|
+
end
|
13
|
+
end
|
@@ -1,19 +1,9 @@
|
|
1
1
|
module ProconBypassMan::Procon::PushedButtonHelper
|
2
|
-
module Static
|
3
|
-
def pressed_button?(button)
|
4
|
-
binary[
|
5
|
-
::ProconBypassMan::Procon::ButtonCollection.load(button).byte_position
|
6
|
-
].unpack("H*").first.to_i(16).to_s(2).reverse[
|
7
|
-
::ProconBypassMan::Procon::ButtonCollection.load(button).bit_position
|
8
|
-
] == '1'
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
2
|
module Dynamic
|
13
3
|
@@compiled = false
|
14
4
|
def compile_if_not_compile_yet!
|
15
5
|
unless @@compiled
|
16
|
-
::ProconBypassMan::Procon::ButtonCollection::BUTTONS_MAP.each do |button,
|
6
|
+
::ProconBypassMan::Procon::ButtonCollection::BUTTONS_MAP.each do |button, _value|
|
17
7
|
define_method "pressed_#{button}?" do
|
18
8
|
pressed_button?(button)
|
19
9
|
end
|