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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +41 -13
  3. data/.github/workflows/ruby.yml +34 -0
  4. data/.rubocop.yml +24 -0
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +15 -0
  7. data/Gemfile +5 -0
  8. data/Gemfile.lock +75 -3
  9. data/README.md +22 -8
  10. data/Rakefile +10 -1
  11. data/Steepfile +39 -0
  12. data/bin/report_receive_server.rb +11 -0
  13. data/docs/setup_raspi.md +12 -7
  14. data/docs/setup_raspi.mitamae.rb +60 -0
  15. data/docs/setup_raspi_by_mitamae.md +14 -0
  16. data/examples/practical/app.rb +2 -2
  17. data/lib/procon_bypass_man/analog_stick_position.rb +14 -0
  18. data/lib/procon_bypass_man/boot_message.rb +40 -0
  19. data/lib/procon_bypass_man/bypass.rb +20 -7
  20. data/lib/procon_bypass_man/configuration/layer.rb +33 -3
  21. data/lib/procon_bypass_man/configuration/loader.rb +17 -16
  22. data/lib/procon_bypass_man/configuration/validator.rb +20 -19
  23. data/lib/procon_bypass_man/configuration.rb +24 -12
  24. data/lib/procon_bypass_man/device_connector.rb +14 -31
  25. data/lib/procon_bypass_man/error_reporter.rb +44 -0
  26. data/lib/procon_bypass_man/io_monitor.rb +7 -4
  27. data/lib/procon_bypass_man/on_memory_cache.rb +34 -0
  28. data/lib/procon_bypass_man/procon/analog_stick_cap.rb +88 -0
  29. data/lib/procon_bypass_man/procon/button_collection.rb +14 -6
  30. data/lib/procon_bypass_man/procon/debug_dumper.rb +17 -0
  31. data/lib/procon_bypass_man/procon/mode_registry.rb +2 -2
  32. data/lib/procon_bypass_man/procon/press_button_aware.rb +13 -0
  33. data/lib/procon_bypass_man/procon/pressed_button_helper.rb +1 -11
  34. data/lib/procon_bypass_man/procon/user_operation.rb +10 -3
  35. data/lib/procon_bypass_man/procon.rb +15 -1
  36. data/lib/procon_bypass_man/reporter.rb +42 -0
  37. data/lib/procon_bypass_man/runner.rb +39 -50
  38. data/lib/procon_bypass_man/uptime.rb +13 -0
  39. data/lib/procon_bypass_man/version.rb +1 -1
  40. data/lib/procon_bypass_man.rb +53 -2
  41. data/procon_bypass_man.gemspec +1 -1
  42. data/project_template/README.md +17 -0
  43. data/project_template/app.rb +20 -0
  44. data/project_template/setting.yml +35 -0
  45. data/project_template/systemd_units/pbm.service +13 -0
  46. data/sig/README.rb +4 -0
  47. data/sig/main.rbs +467 -0
  48. metadata +27 -8
  49. data/examples/pbm.service +0 -27
  50. 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, &block)
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
- validation_instance = ProconBypassMan::Configuration.switch_context(:validation) do |instance|
6
- begin
7
- yaml = YAML.load_file(setting_path) or raise "読み込みに失敗しました"
8
- instance.instance_eval(yaml["setting"])
9
- rescue SyntaxError
10
- instance.errors[:base] << "Rubyのシンタックスエラーです"
11
- next(instance)
12
- rescue Psych::SyntaxError
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
- instance.valid?
17
- next(instance)
18
- end
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
- module Validator
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.instance_eval { @flips.keys }.map(&:to_sym).each { |b| unverified_buttons << b }
52
- value.instance_eval { @remaps.keys }.map(&:to_sym).each { |b| unverified_buttons << b }
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.instance_eval {
55
- @flips.flat_map { |flip_button, flip_option|
56
- flip_option.flat_map { |flip_option_key, flip_option_target_button|
57
- next if flip_option_key == :flip_interval
58
- next if flip_option_target_button.is_a?(FalseClass) || flip_option_target_button.is_a?(TrueClass)
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.instance_eval {
64
- @remaps.flat_map { |button, option|
65
- option.flat_map { |flip_option_key, flip_option_target_button|
66
- next if flip_option_target_button.is_a?(FalseClass) || flip_option_target_button.is_a?(TrueClass)
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.instance_eval { @flips.keys }.map(&:to_sym).each { |b| flip_buttons << b }
82
- value.instance_eval { @remaps.keys }.map(&:to_sym).each { |b| remap_buttons << b }
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.switch_context(key)
23
- @@context[key] ||= new
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
- MODES = [:manual]
41
- def layer(direction, mode: :manual, &block)
42
- if mode.respond_to?(:name)
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(128)
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(128)
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(128)
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(128)
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(128)
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(128)
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
- print "\r"
66
- print " " * max_output_length
67
- print "\r"
68
- print line
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, value|
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