Class: Fusuma::Plugin::Inputs::Hidraw::HhkbBluetoothParser
- Inherits:
-
Object
- Object
- Fusuma::Plugin::Inputs::Hidraw::HhkbBluetoothParser
- Defined in:
- lib/fusuma/plugin/inputs/hidraw/hhkb_bluetooth_parser.rb
Constant Summary collapse
- BASE_TIMEOUT =
Base timeout value for reading reports
0.03
- MAX_TIMEOUT =
Maximum timeout value before failure
0.2
- MULTIPLIER =
Multiplier to exponentially increase timeout
1.1
- MAX_REPORT_SIZE =
Maximum report size in bytes
9
Instance Method Summary collapse
-
#initialize(hidraw_device) ⇒ HhkbBluetoothParser
constructor
A new instance of HhkbBluetoothParser.
-
#parse ⇒ Object
Parse HID raw device events.
-
#parse_hid_report(report_bytes) ⇒ Symbol?
Parse the HID report to determine its type.
-
#parse_keyboard_report(report_bytes) ⇒ Object
Parse keyboard report data.
-
#parse_mouse_report(report_bytes) ⇒ Object
Parse mouse report data.
-
#read_with_timeout(device, timeout) ⇒ String
Reads the HID report from the device with a timeout.
-
#translate_keycode(keycode) ⇒ String
Translate keycode to its string representation.
-
#update_timeout(timeout) ⇒ Float
Update the timeout based on previous value.
Constructor Details
#initialize(hidraw_device) ⇒ HhkbBluetoothParser
Returns a new instance of HhkbBluetoothParser.
15 16 17 |
# File 'lib/fusuma/plugin/inputs/hidraw/hhkb_bluetooth_parser.rb', line 15 def initialize(hidraw_device) @hidraw_device = hidraw_device end |
Instance Method Details
#parse ⇒ Object
Parse HID raw device events.
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/fusuma/plugin/inputs/hidraw/hhkb_bluetooth_parser.rb', line 20 def parse File.open(@hidraw_device.hidraw_path, "rb") do |device| timeout = nil # Continuously read reports from the device. while (report = read_with_timeout(device, timeout)) mouse_state = if report.empty? # Handle timeout case :end else case parse_hid_report(report) when :mouse case mouse_state when :begin, :update :update else :begin end when :keyboard # Continue mouse_state when keyboard operation mouse_state else :end end end case mouse_state when :begin, :update timeout = update_timeout(timeout) when :end timeout = nil end yield mouse_state end end end |
#parse_hid_report(report_bytes) ⇒ Symbol?
Parse the HID report to determine its type.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/fusuma/plugin/inputs/hidraw/hhkb_bluetooth_parser.rb', line 81 def parse_hid_report(report_bytes) report_id = report_bytes.getbyte(0) case report_id when 1 # parse_mouse_report(report_bytes) :mouse when 127 # parse_keyboard_report(report_bytes) :keyboard else MultiLogger.warn "Unknown Report ID: #{report_id}" nil end end |
#parse_keyboard_report(report_bytes) ⇒ Object
Parse keyboard report data.
115 116 117 118 119 120 121 122 123 124 |
# File 'lib/fusuma/plugin/inputs/hidraw/hhkb_bluetooth_parser.rb', line 115 def parse_keyboard_report(report_bytes) report_id, modifiers, _reserved1, *keys = report_bytes.unpack("CCCC6") # Retrieve 9-byte report # - `C`: 1 byte unsigned integer (report ID) (0..255) # - `C`: 1 byte unsigned integer (modifier keys) (0..255) # - `C`: 1 byte reserved (0) # - `C`: 6 bytes of keycodes (0..255) modifier_states = %w[LeftControl LeftShift LeftAlt LeftGUI RightControl RightShift RightAlt RightGUI].map.with_index { |m, i| "#{m}: #{((modifiers & (1 << i)) != 0) ? 1 : 0}" } keys_output = keys.map { |key| (key == 0) ? "0x70000" : translate_keycode(key) } puts "# ReportID: #{report_id} / #{modifier_states.join(" | ")} | Keyboard #{keys_output}" end |
#parse_mouse_report(report_bytes) ⇒ Object
Parse mouse report data.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/fusuma/plugin/inputs/hidraw/hhkb_bluetooth_parser.rb', line 98 def parse_mouse_report(report_bytes) puts "Raw bytes: #{report_bytes.inspect}" # Display raw byte bytes report_id, , x, y, wheel, ac_pan = report_bytes.unpack("CCcccc") # Retrieve 6-byte report # - `C`: 1 byte unsigned integer (report ID) (0..255) # - `C`: 1 byte unsigned integer (button state) (0..255) # - `c`: 1 byte signed integer (x-axis) (-128..127) # - `c`: 1 byte signed integer (y-axis) (-128..127) # - `c`: 1 byte signed integer (wheel) (-128..127) # - `c`: 1 byte signed integer (AC pan) (-128..127) = .to_s(2).rjust(8, "0").chars.map(&:to_i) puts "# ReportID: #{report_id} / Button: #{.join(" ")} | X: #{x.to_s.rjust(4)} | Y: #{y.to_s.rjust(4)} | Wheel: #{wheel.to_s.rjust(4)} | AC Pan: #{ac_pan.to_s.rjust(4)}" end |
#read_with_timeout(device, timeout) ⇒ String
Reads the HID report from the device with a timeout.
62 63 64 65 66 67 |
# File 'lib/fusuma/plugin/inputs/hidraw/hhkb_bluetooth_parser.rb', line 62 def read_with_timeout(device, timeout) # puts "Timeout: #{timeout}" # Log timeout for debugging Timeout.timeout(timeout) { device.read(MAX_REPORT_SIZE) } rescue Timeout::Error "" end |
#translate_keycode(keycode) ⇒ String
Translate keycode to its string representation.
129 130 131 132 133 134 135 136 137 138 |
# File 'lib/fusuma/plugin/inputs/hidraw/hhkb_bluetooth_parser.rb', line 129 def translate_keycode(keycode) # Map of keycodes to their respective characters keycodes = { 4 => "a and A", 7 => "d and D", 16 => "s and S", 19 => "w and W", 9 => "f and F", 10 => "g and G", 14 => "j and J", 15 => "k and K", 33 => "[ and {", 47 => "] and }" # Add more as needed } keycodes[keycode] || "0x#{keycode.to_s(16)}" # Return hexadecimal if not found end |
#update_timeout(timeout) ⇒ Float
Update the timeout based on previous value.
72 73 74 75 76 |
# File 'lib/fusuma/plugin/inputs/hidraw/hhkb_bluetooth_parser.rb', line 72 def update_timeout(timeout) return BASE_TIMEOUT if timeout.nil? [timeout * MULTIPLIER, MAX_TIMEOUT].min end |