procon_bypass_man 0.1.8 → 0.1.12
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 +3 -2
- data/.github/workflows/ruby.yml +5 -4
- data/.gitignore +5 -0
- data/.rubocop.yml +2 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +18 -1
- data/Gemfile +4 -0
- data/Gemfile.lock +53 -2
- data/README.md +14 -8
- data/Steepfile +39 -0
- data/bin/console +4 -0
- data/bin/dev_api_server.rb +18 -0
- data/docs/setup_raspi.mitamae.rb +12 -0
- data/lib/procon_bypass_man/background/has_server_pool.rb +54 -0
- data/lib/procon_bypass_man/background/http_client.rb +70 -0
- data/lib/procon_bypass_man/background/job_performer.rb +16 -0
- data/lib/procon_bypass_man/background/job_runnable.rb +16 -0
- data/lib/procon_bypass_man/background/job_runner.rb +44 -0
- data/lib/procon_bypass_man/background/jobs/base_job.rb +12 -0
- data/lib/procon_bypass_man/background/jobs/report_boot_job.rb +10 -0
- data/lib/procon_bypass_man/background/jobs/report_error_job.rb +10 -0
- data/lib/procon_bypass_man/background/jobs/report_heartbeat_job.rb +10 -0
- data/lib/procon_bypass_man/background/jobs/report_pressed_buttons_job.rb +18 -0
- data/lib/procon_bypass_man/background/jobs/report_reload_config_job.rb +10 -0
- data/lib/procon_bypass_man/background.rb +10 -0
- data/lib/procon_bypass_man/boot_message.rb +42 -0
- data/lib/procon_bypass_man/{configuration → buttons_setting_configuration}/layer.rb +50 -4
- data/lib/procon_bypass_man/{configuration → buttons_setting_configuration}/loader.rb +12 -11
- data/lib/procon_bypass_man/{configuration → buttons_setting_configuration}/validator.rb +1 -1
- data/lib/procon_bypass_man/buttons_setting_configuration.rb +101 -0
- data/lib/procon_bypass_man/bypass/usb_hid_logger.rb +36 -0
- data/lib/procon_bypass_man/bypass.rb +61 -29
- data/lib/procon_bypass_man/callbacks.rb +70 -0
- data/lib/procon_bypass_man/commands/connect_device_command.rb +11 -0
- data/lib/procon_bypass_man/commands/print_boot_message_command.rb +9 -0
- data/lib/procon_bypass_man/commands/send_error_command.rb +18 -0
- data/lib/procon_bypass_man/commands/send_reload_config_event_command.rb +10 -0
- data/lib/procon_bypass_man/commands/write_device_id_command.rb +11 -0
- data/lib/procon_bypass_man/commands/write_session_id_command.rb +13 -0
- data/lib/procon_bypass_man/commands.rb +6 -0
- data/lib/procon_bypass_man/configuration.rb +92 -67
- data/lib/procon_bypass_man/device_connector.rb +11 -29
- data/lib/procon_bypass_man/io_monitor.rb +16 -8
- data/lib/procon_bypass_man/on_memory_cache.rb +34 -0
- data/lib/procon_bypass_man/procon/analog_stick.rb +31 -0
- data/lib/procon_bypass_man/procon/analog_stick_cap.rb +65 -0
- data/lib/procon_bypass_man/procon/button_collection.rb +15 -6
- data/lib/procon_bypass_man/procon/{data.rb → consts.rb} +1 -1
- data/lib/procon_bypass_man/procon/layer_changer.rb +40 -0
- data/lib/procon_bypass_man/procon/macro_registry.rb +2 -2
- data/lib/procon_bypass_man/procon/mode_registry.rb +4 -4
- data/lib/procon_bypass_man/procon/press_button_aware.rb +13 -0
- data/lib/procon_bypass_man/procon/user_operation.rb +21 -16
- data/lib/procon_bypass_man/procon.rb +23 -9
- data/lib/procon_bypass_man/procon_reader.rb +31 -0
- data/lib/procon_bypass_man/runner.rb +43 -64
- data/lib/procon_bypass_man/uptime.rb +14 -2
- data/lib/procon_bypass_man/version.rb +1 -1
- data/lib/procon_bypass_man.rb +38 -43
- data/project_template/README.md +1 -1
- data/project_template/app.rb +7 -5
- data/project_template/systemd_units/pbm_web.service +11 -0
- data/project_template/web.rb +16 -0
- data/sig/README.rb +4 -0
- data/sig/main.rbs +505 -0
- metadata +42 -11
- data/examples/practical/app.rb +0 -21
- data/examples/practical/setting.yml +0 -24
- data/lib/procon_bypass_man/procon/layer_changeable.rb +0 -28
- data/lib/procon_bypass_man/procon/pressed_button_helper.rb +0 -25
| @@ -1,86 +1,111 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
            module ProconBypassMan
         | 
| 6 | 
            -
              class Configuration
         | 
| 7 | 
            -
             | 
| 8 | 
            -
                attr_accessor :layers,
         | 
| 9 | 
            -
                  :setting_path,
         | 
| 10 | 
            -
                  :mode_plugins,
         | 
| 11 | 
            -
                  :macro_plugins,
         | 
| 12 | 
            -
                  :context,
         | 
| 13 | 
            -
                  :current_context_key
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                def self.instance
         | 
| 16 | 
            -
                  @@current_context_key ||= :main
         | 
| 17 | 
            -
                  @@context ||= {}
         | 
| 18 | 
            -
                  @@context[@@current_context_key] ||= new
         | 
| 1 | 
            +
            class ProconBypassMan::Configuration
         | 
| 2 | 
            +
              module ClassMethods
         | 
| 3 | 
            +
                def root
         | 
| 4 | 
            +
                  config.root
         | 
| 19 5 | 
             
                end
         | 
| 20 6 |  | 
| 21 | 
            -
                def  | 
| 22 | 
            -
                   | 
| 23 | 
            -
                  previous_key = @@current_context_key
         | 
| 24 | 
            -
                  if block_given?
         | 
| 25 | 
            -
                    @@current_context_key = key
         | 
| 26 | 
            -
                    value = yield(@@context[key])
         | 
| 27 | 
            -
                    @@current_context_key = previous_key
         | 
| 28 | 
            -
                    return value
         | 
| 29 | 
            -
                  else
         | 
| 30 | 
            -
                    @@current_context_key = key
         | 
| 31 | 
            -
                  end
         | 
| 7 | 
            +
                def logger
         | 
| 8 | 
            +
                  config.logger
         | 
| 32 9 | 
             
                end
         | 
| 33 10 |  | 
| 34 | 
            -
                def  | 
| 35 | 
            -
                   | 
| 11 | 
            +
                def error_logger
         | 
| 12 | 
            +
                  config.error_logger
         | 
| 36 13 | 
             
                end
         | 
| 37 14 |  | 
| 38 | 
            -
                 | 
| 39 | 
            -
             | 
| 40 | 
            -
                  if mode.respond_to?(:name)
         | 
| 41 | 
            -
                    mode_name = mode.name.to_sym
         | 
| 42 | 
            -
                  else
         | 
| 43 | 
            -
                    mode_name = mode
         | 
| 44 | 
            -
                  end
         | 
| 45 | 
            -
                  unless (MODES + ProconBypassMan::Procon::ModeRegistry.plugins.keys).include?(mode_name)
         | 
| 46 | 
            -
                    raise("#{mode_name} mode is unknown")
         | 
| 47 | 
            -
                  end
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                  layer = Layer.new(mode: mode_name)
         | 
| 50 | 
            -
                  layer.instance_eval(&block) if block_given?
         | 
| 51 | 
            -
                  self.layers[direction] = layer
         | 
| 52 | 
            -
                  self
         | 
| 15 | 
            +
                def pid_path
         | 
| 16 | 
            +
                  @@pid_path ||= File.expand_path("#{root}/pbm_pid", __dir__).freeze
         | 
| 53 17 | 
             
                end
         | 
| 54 18 |  | 
| 55 | 
            -
                def  | 
| 56 | 
            -
                   | 
| 57 | 
            -
                  self
         | 
| 19 | 
            +
                def digest_path
         | 
| 20 | 
            +
                  config.digest_path
         | 
| 58 21 | 
             
                end
         | 
| 59 22 |  | 
| 60 | 
            -
                def  | 
| 61 | 
            -
                  ProconBypassMan:: | 
| 62 | 
            -
                  self
         | 
| 23 | 
            +
                def cache
         | 
| 24 | 
            +
                  @@cache_table ||= ProconBypassMan::OnMemoryCache.new
         | 
| 63 25 | 
             
                end
         | 
| 64 26 |  | 
| 65 | 
            -
                 | 
| 66 | 
            -
             | 
| 67 | 
            -
                   | 
| 27 | 
            +
                # @return [String]
         | 
| 28 | 
            +
                def session_id
         | 
| 29 | 
            +
                  ProconBypassMan::WriteSessionIdCommand.execute
         | 
| 68 30 | 
             
                end
         | 
| 69 31 |  | 
| 70 | 
            -
                 | 
| 71 | 
            -
             | 
| 32 | 
            +
                # @return [String]
         | 
| 33 | 
            +
                def device_id
         | 
| 34 | 
            +
                  ProconBypassMan::WriteDeviceIdCommand.execute
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              attr_accessor :enable_critical_error_logging, :raw_setting
         | 
| 39 | 
            +
              attr_writer :verbose_bypass_log
         | 
| 40 | 
            +
             | 
| 41 | 
            +
              def root=(path)
         | 
| 42 | 
            +
                @root = path
         | 
| 43 | 
            +
                return self
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              def root
         | 
| 47 | 
            +
                if defined?(@root)
         | 
| 48 | 
            +
                  @root
         | 
| 49 | 
            +
                else
         | 
| 50 | 
            +
                  File.expand_path('..', __dir__ || ".").freeze
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              def api_servers=(api_servers)
         | 
| 55 | 
            +
                @api_servers = api_servers
         | 
| 56 | 
            +
                return self
         | 
| 57 | 
            +
              end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
              def logger=(logger)
         | 
| 60 | 
            +
                @logger = logger
         | 
| 61 | 
            +
                return self
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              def logger
         | 
| 65 | 
            +
                if ENV["PBM_ENV"] == 'test'
         | 
| 66 | 
            +
                  return Logger.new($stdout)
         | 
| 72 67 | 
             
                end
         | 
| 73 68 |  | 
| 74 | 
            -
                 | 
| 75 | 
            -
                  @ | 
| 76 | 
            -
             | 
| 77 | 
            -
                   | 
| 78 | 
            -
                  self.layers = {
         | 
| 79 | 
            -
                    up: Layer.new,
         | 
| 80 | 
            -
                    down: Layer.new,
         | 
| 81 | 
            -
                    left: Layer.new,
         | 
| 82 | 
            -
                    right: Layer.new,
         | 
| 83 | 
            -
                  }
         | 
| 69 | 
            +
                if defined?(@logger) && @logger.is_a?(Logger)
         | 
| 70 | 
            +
                  @logger
         | 
| 71 | 
            +
                else
         | 
| 72 | 
            +
                  Logger.new(File.open("/dev/null"))
         | 
| 84 73 | 
             
                end
         | 
| 85 74 | 
             
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              def error_logger
         | 
| 77 | 
            +
                if enable_critical_error_logging
         | 
| 78 | 
            +
                  @@error_logger ||= Logger.new("#{ProconBypassMan.root}/error.log", 5, 1024 * 1024 * 10)
         | 
| 79 | 
            +
                else
         | 
| 80 | 
            +
                  Logger.new(File.open("/dev/null"))
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
                self
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
              def digest_path
         | 
| 86 | 
            +
                "#{root}/.setting_yaml_digest"
         | 
| 87 | 
            +
              end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              # @return [String] pbm-webの接続先
         | 
| 90 | 
            +
              def internal_api_servers
         | 
| 91 | 
            +
                if !!ENV["INTERNAL_API_SERVER"]
         | 
| 92 | 
            +
                  [ENV["INTERNAL_API_SERVER"]]
         | 
| 93 | 
            +
                else
         | 
| 94 | 
            +
                  [ 'http://localhost:9090',
         | 
| 95 | 
            +
                    'http://localhost:8080',
         | 
| 96 | 
            +
                  ].compact
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
              end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
              def api_servers
         | 
| 101 | 
            +
                if !!ENV["API_SERVER"]
         | 
| 102 | 
            +
                  [ENV["API_SERVER"]].reject(&:nil?)
         | 
| 103 | 
            +
                else
         | 
| 104 | 
            +
                  [@api_servers].flatten.reject(&:nil?)
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
              end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
              def verbose_bypass_log
         | 
| 109 | 
            +
                @verbose_bypass_log || !!ENV["VERBOSE_BYPASS_LOG"]
         | 
| 110 | 
            +
              end
         | 
| 86 111 | 
             
            end
         | 
| @@ -12,23 +12,6 @@ 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 15 | 
             
              def self.connect
         | 
| 33 16 | 
             
                s = new(throw_error_if_timeout: true, enable_at_exit: false)
         | 
| 34 17 | 
             
                s.add([
         | 
| @@ -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
         | 
| @@ -326,8 +309,7 @@ class ProconBypassMan::DeviceConnector | |
| 326 309 | 
             
                end
         | 
| 327 310 | 
             
              rescue Errno::ENXIO => e
         | 
| 328 311 | 
             
                # /dev/hidg0 をopenできないときがある
         | 
| 329 | 
            -
                ProconBypassMan. | 
| 330 | 
            -
                ProconBypassMan.logger.error e
         | 
| 312 | 
            +
                ProconBypassMan::SendErrorCommand.execute(error: "Errno::ENXIO (No such device or address @ rb_sysopen - /dev/hidg0)が起きました。resetします. #{e.full_message}")
         | 
| 331 313 | 
             
                system('echo > /sys/kernel/config/usb_gadget/procon/UDC')
         | 
| 332 314 | 
             
                system('ls /sys/class/udc > /sys/kernel/config/usb_gadget/procon/UDC')
         | 
| 333 315 | 
             
                sleep 2
         | 
| @@ -1,11 +1,12 @@ | |
| 1 1 | 
             
            module ProconBypassMan
         | 
| 2 2 | 
             
              class Counter
         | 
| 3 | 
            -
                attr_accessor :label, :table, :previous_table
         | 
| 3 | 
            +
                attr_accessor :label, :table, :previous_table, :active
         | 
| 4 4 |  | 
| 5 5 | 
             
                def initialize(label: )
         | 
| 6 6 | 
             
                  self.label = label
         | 
| 7 7 | 
             
                  self.table = {}
         | 
| 8 8 | 
             
                  self.previous_table = {}
         | 
| 9 | 
            +
                  self.active = true
         | 
| 9 10 | 
             
                end
         | 
| 10 11 |  | 
| 11 12 | 
             
                # アクティブなバケットは1つだけ
         | 
| @@ -24,7 +25,7 @@ module ProconBypassMan | |
| 24 25 | 
             
                  self
         | 
| 25 26 | 
             
                end
         | 
| 26 27 |  | 
| 27 | 
            -
                def  | 
| 28 | 
            +
                def formatted_previous_table
         | 
| 28 29 | 
             
                  t = previous_table.dup
         | 
| 29 30 | 
             
                  start_function = t[:start_function] || 0
         | 
| 30 31 | 
             
                  end_function = t[:end_function] || 0
         | 
| @@ -32,6 +33,10 @@ module ProconBypassMan | |
| 32 33 | 
             
                  eagain_wait_readable_on_write = t[:eagain_wait_readable_on_write] || 0
         | 
| 33 34 | 
             
                  "(#{(end_function / start_function.to_f * 100).floor(1)}%(#{end_function}/#{start_function}), loss: #{eagain_wait_readable_on_read}, #{eagain_wait_readable_on_write})"
         | 
| 34 35 | 
             
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def shutdown
         | 
| 38 | 
            +
                  self.active = false
         | 
| 39 | 
            +
                end
         | 
| 35 40 | 
             
              end
         | 
| 36 41 |  | 
| 37 42 | 
             
              module IOMonitor
         | 
| @@ -51,21 +56,24 @@ module ProconBypassMan | |
| 51 56 | 
             
                  Thread.start do
         | 
| 52 57 | 
             
                    max_output_length = 0
         | 
| 53 58 | 
             
                    loop do
         | 
| 54 | 
            -
                      list = @@list.dup
         | 
| 59 | 
            +
                      list = @@list.select(&:active).dup
         | 
| 55 60 | 
             
                      unless list.all? { |x| x&.previous_table.is_a?(Hash) }
         | 
| 56 61 | 
             
                        sleep 0.5
         | 
| 57 62 | 
             
                        next
         | 
| 58 63 | 
             
                      end
         | 
| 59 64 |  | 
| 60 65 | 
             
                      line = list.map { |counter|
         | 
| 61 | 
            -
                        "#{counter.label}(#{counter. | 
| 66 | 
            +
                        "#{counter.label}(#{counter.formatted_previous_table})"
         | 
| 62 67 | 
             
                      }.join(", ")
         | 
| 63 68 | 
             
                      max_output_length = line.length
         | 
| 64 69 | 
             
                      sleep 0.7
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                       | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 70 | 
            +
             | 
| 71 | 
            +
                      if ENV["PBM_FOREGROUND"]
         | 
| 72 | 
            +
                        print "\r"
         | 
| 73 | 
            +
                        print " " * max_output_length
         | 
| 74 | 
            +
                        print "\r"
         | 
| 75 | 
            +
                        print line
         | 
| 76 | 
            +
                      end
         | 
| 69 77 | 
             
                      ProconBypassMan.logger.debug { line }
         | 
| 70 78 | 
             
                      break if $will_terminate_token
         | 
| 71 79 | 
             
                    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,31 @@ | |
| 1 | 
            +
            class ProconBypassMan::Procon::AnalogStick
         | 
| 2 | 
            +
              attr_accessor :neutral_position
         | 
| 3 | 
            +
              attr_accessor :bin_x, :bin_y
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              def initialize(binary: )
         | 
| 6 | 
            +
                @neutral_position = ProconBypassMan::ButtonsSettingConfiguration.instance.neutral_position
         | 
| 7 | 
            +
             | 
| 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 | 
            +
                self.bin_x = "#{byte7[4..7]}#{byte6}"
         | 
| 13 | 
            +
                self.bin_y = "#{byte8}#{byte7[0..3]}"
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              def abs_x
         | 
| 17 | 
            +
                bin_x.to_i(2)
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              def abs_y
         | 
| 21 | 
            +
                bin_y.to_i(2)
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              def relative_x
         | 
| 25 | 
            +
                bin_x.to_i(2) - neutral_position.x
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              def relative_y
         | 
| 29 | 
            +
                bin_y.to_i(2) - neutral_position.y
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
| @@ -0,0 +1,65 @@ | |
| 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 | 
            +
              def initialize(binary)
         | 
| 22 | 
            +
                @binary = binary
         | 
| 23 | 
            +
                @analog_stick = ProconBypassMan::Procon::AnalogStick.new(binary: binary)
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              # @return [ProconBypassMan::Procon::AnalogStickCap::Position]
         | 
| 27 | 
            +
              def capped_position(cap_hypotenuse: )
         | 
| 28 | 
            +
                if hypotenuse > cap_hypotenuse
         | 
| 29 | 
            +
                  relative_capped_x = cap_hypotenuse * Math.cos(rad * Math::PI / 180).abs
         | 
| 30 | 
            +
                  relative_capped_y = cap_hypotenuse * Math.sin(rad * Math::PI / 180).abs
         | 
| 31 | 
            +
                  relative_capped_x = -(relative_capped_x.abs) if relative_x.negative?
         | 
| 32 | 
            +
                  relative_capped_y = -(relative_capped_y.abs) if relative_y.negative?
         | 
| 33 | 
            +
                  return Position.new(
         | 
| 34 | 
            +
                    x: relative_capped_x + @analog_stick.neutral_position.x,
         | 
| 35 | 
            +
                    y: relative_capped_y + @analog_stick.neutral_position.y,
         | 
| 36 | 
            +
                  )
         | 
| 37 | 
            +
                else
         | 
| 38 | 
            +
                  return position
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              # @return [ProconBypassMan::Procon::AnalogStickCap::Position]
         | 
| 43 | 
            +
              def position
         | 
| 44 | 
            +
                Position.new(x: abs_x, y: abs_y)
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              def abs_x; @analog_stick.abs_x; end # 0, 0からのx
         | 
| 48 | 
            +
              def abs_y; @analog_stick.abs_y; end # 0, 0からのy
         | 
| 49 | 
            +
              def relative_x; @analog_stick.relative_x; end
         | 
| 50 | 
            +
              def relative_y; @analog_stick.relative_y; end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              # @deprecated
         | 
| 53 | 
            +
              def x; relative_x; end
         | 
| 54 | 
            +
              def y; relative_y; end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              def rad
         | 
| 57 | 
            +
                (
         | 
| 58 | 
            +
                  Math.atan(relative_y / relative_x.to_f) * 180 / Math::PI
         | 
| 59 | 
            +
                ).floor(6)
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
              def hypotenuse
         | 
| 63 | 
            +
                Math.sqrt(relative_x**2 + relative_y**2).floor(6)
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
            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,11 +29,15 @@ 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|
         | 
| 30 38 | 
             
                next acc if value[1].nil?
         | 
| 31 39 | 
             
                value[1].reverse.each.with_index do |button, index|
         | 
| 40 | 
            +
                  next(acc) if button == :grip || button == :_undefined_key
         | 
| 32 41 | 
             
                  acc[button] = { byte_position: value[0], bit_position: index }
         | 
| 33 42 | 
             
                end
         | 
| 34 43 | 
             
                acc
         | 
| @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            class ProconBypassMan::Procon::LayerChanger
         | 
| 2 | 
            +
              def initialize(binary: )
         | 
| 3 | 
            +
                @procon_reader = ProconBypassMan::ProconReader.new(binary: binary)
         | 
| 4 | 
            +
              end
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              # @return [Symbol]
         | 
| 7 | 
            +
              def next_layer_key
         | 
| 8 | 
            +
                case
         | 
| 9 | 
            +
                when pressed?(button: :up)
         | 
| 10 | 
            +
                  :up
         | 
| 11 | 
            +
                when pressed?(button: :right)
         | 
| 12 | 
            +
                  :right
         | 
| 13 | 
            +
                when pressed?(button: :left)
         | 
| 14 | 
            +
                  :left
         | 
| 15 | 
            +
                when pressed?(button: :down)
         | 
| 16 | 
            +
                  :down
         | 
| 17 | 
            +
                else
         | 
| 18 | 
            +
                  ProconBypassMan.logger.warn("next_layer_key is unknown")
         | 
| 19 | 
            +
                  :up
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              # @return [Boolean]
         | 
| 24 | 
            +
              def change_layer?
         | 
| 25 | 
            +
                if ProconBypassMan::ButtonsSettingConfiguration.instance.prefix_keys.empty?
         | 
| 26 | 
            +
                  raise "prefix_keysが未設定です"
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
                ProconBypassMan::ButtonsSettingConfiguration.instance.prefix_keys.map { |b| pressed?(button: b) }.all?
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              # @return [Boolean]
         | 
| 32 | 
            +
              def pressed_next_layer?
         | 
| 33 | 
            +
                change_layer? && (pressed?(button: :up) || pressed?(button: :right) || pressed?(button: :left) || pressed?(button: :down))
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              # @return [Boolean]
         | 
| 37 | 
            +
              def pressed?(button: )
         | 
| 38 | 
            +
                @procon_reader.pressed.include?(button)
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
| @@ -37,11 +37,11 @@ class ProconBypassMan::Procon::MacroRegistry | |
| 37 37 | 
             
              end
         | 
| 38 38 |  | 
| 39 39 | 
             
              def self.reset!
         | 
| 40 | 
            -
                ProconBypassMan:: | 
| 40 | 
            +
                ProconBypassMan::ButtonsSettingConfiguration.instance.macro_plugins = {}
         | 
| 41 41 | 
             
              end
         | 
| 42 42 |  | 
| 43 43 | 
             
              def self.plugins
         | 
| 44 | 
            -
                ProconBypassMan:: | 
| 44 | 
            +
                ProconBypassMan::ButtonsSettingConfiguration.instance.macro_plugins
         | 
| 45 45 | 
             
              end
         | 
| 46 46 |  | 
| 47 47 | 
             
              reset!
         | 
| @@ -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)
         | 
| @@ -35,11 +35,11 @@ class ProconBypassMan::Procon::ModeRegistry | |
| 35 35 | 
             
              end
         | 
| 36 36 |  | 
| 37 37 | 
             
              def self.reset!
         | 
| 38 | 
            -
                ProconBypassMan:: | 
| 38 | 
            +
                ProconBypassMan::ButtonsSettingConfiguration.instance.mode_plugins = {}
         | 
| 39 39 | 
             
              end
         | 
| 40 40 |  | 
| 41 41 | 
             
              def self.plugins
         | 
| 42 | 
            -
                ProconBypassMan:: | 
| 42 | 
            +
                ProconBypassMan::ButtonsSettingConfiguration.instance.mode_plugins
         | 
| 43 43 | 
             
              end
         | 
| 44 44 |  | 
| 45 45 | 
             
              reset!
         | 
| @@ -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
         |