procon_bypass_man 0.1.5 → 0.1.9
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/.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
|