From a0352a2accc31efdcc1a9693f6adf761bd635fe0 Mon Sep 17 00:00:00 2001 From: Jeremy Mickelson Date: Sun, 3 Jan 2021 00:07:31 -0800 Subject: [PATCH 1/2] support sending a serial number to hid_open, requires support for writing wide strings --- hid_api.gemspec | 1 + lib/hid_api.rb | 7 ++----- lib/hid_api/device.rb | 15 ++++++-------- lib/hid_api/device_info.rb | 2 +- lib/hid_api/util/wchar.rb | 40 -------------------------------------- lib/hid_api/wide_string.rb | 7 ++++++- 6 files changed, 16 insertions(+), 56 deletions(-) delete mode 100644 lib/hid_api/util/wchar.rb diff --git a/hid_api.gemspec b/hid_api.gemspec index 9cce1d8..b88d5cd 100644 --- a/hid_api.gemspec +++ b/hid_api.gemspec @@ -22,6 +22,7 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency "ffi", "~> 1.9" + spec.add_dependency "ffi_wide_char", "~> 1.1" spec.add_development_dependency "bundler", "~> 1.8" spec.add_development_dependency "rake", "~> 10.0" diff --git a/lib/hid_api.rb b/lib/hid_api.rb index 5196746..47ecc8e 100644 --- a/lib/hid_api.rb +++ b/lib/hid_api.rb @@ -19,7 +19,7 @@ class HidError < StandardError; end typedef Device, :device typedef :int, :vendor_id typedef :int, :product_id - typedef :int, :serial_number + typedef WideString, :serial_number typedef :int, :length typedef :int, :timeout @@ -49,7 +49,7 @@ class << self alias enumerate hid_enumerate alias free_enumeration hid_free_enumeration def open(vendor_id, product_id, serial_number = nil) - device = hid_open(vendor_id, product_id, serial_number || 0) + device = hid_open(vendor_id, product_id, serial_number) if device.null? error = format( "Unable to open %s", @@ -67,6 +67,3 @@ def open_path(path) end end end - -# Attempts to extend some core FFI classes with platform-aware string-handling -FFI::AbstractMemory.include(HidApi::Util::WCHAR) diff --git a/lib/hid_api/device.rb b/lib/hid_api/device.rb index 90a09a5..698ff54 100644 --- a/lib/hid_api/device.rb +++ b/lib/hid_api/device.rb @@ -38,7 +38,7 @@ def serial_number_string def read(length) buffer = clear_buffer length with_hid_error_handling do - HidApi.hid_read self, buffer, buffer.length + HidApi.hid_read self, buffer, buffer.size end buffer end @@ -46,7 +46,7 @@ def read(length) def read_timeout(length, timeout) buffer = clear_buffer length with_hid_error_handling do - HidApi.hid_read_timeout self, buffer, buffer.length, timeout + HidApi.hid_read_timeout self, buffer, buffer.size, timeout end buffer end @@ -59,7 +59,7 @@ def write(data) end with_hid_error_handling do - HidApi.hid_write self, buffer, buffer.length + HidApi.hid_write self, buffer, buffer.size end end @@ -80,16 +80,13 @@ def error private def clear_buffer(length) - b = FFI::Buffer.new(1, length) - # FFI::Buffer doesn't clear the first byte if length < 8 - b.put_char 0, 0 - b + return FFI::MemoryPointer.new(:char, length) end def get_buffered_string(field) buffer = clear_buffer 255 - HidApi.send "hid_get_#{field}_string", self, buffer, buffer.length - buffer.read_wchar_string + HidApi.send "hid_get_#{field}_string", self, buffer, buffer.size + FfiWideChar.read_wide_string buffer end def with_hid_error_handling diff --git a/lib/hid_api/device_info.rb b/lib/hid_api/device_info.rb index 0206ce0..68eb1fa 100644 --- a/lib/hid_api/device_info.rb +++ b/lib/hid_api/device_info.rb @@ -28,7 +28,7 @@ def self.release(pointer) :next, DeviceInfo.auto_ptr # struct hid_device_info * next # Makes the struct members available as methods - layout.members.each do |f| + members.each do |f| define_method(f) do self[f] end diff --git a/lib/hid_api/util/wchar.rb b/lib/hid_api/util/wchar.rb deleted file mode 100644 index 51415f9..0000000 --- a/lib/hid_api/util/wchar.rb +++ /dev/null @@ -1,40 +0,0 @@ -module HidApi - module Util - # A utility module that provides simple (maybe naive) platform-dependent - # support for wide characters - module WCHAR - WCHAR_T_WIDTH = ::RUBY_PLATFORM =~ /mswin/i ? 2 : 4 - - # Maps a WCHAR_T_WIDTH to the corresponding Array#pack character width - FORMATS = { - 2 => "S", - 4 => "L" - }.freeze - - # The platform-specific Array#pack format for wide characters - FORMAT = FORMATS[WCHAR_T_WIDTH].freeze - - # Maps a WCHAR_T_WIDTH to the corresponding string encoding - ENCODINGS = { - 2 => "utf-16le", - 4 => "utf-32le" - }.freeze - - # The platform-specific String encoding that can handle wide characters - ENCODING = ENCODINGS[WCHAR_T_WIDTH].freeze - - def read_wchar_string(_max_chars = nil) - buffer = [] - offset = 0 - loop do - pointer = self + (offset * WCHAR_T_WIDTH) - char = pointer.send("read_uint#{WCHAR_T_WIDTH * 8}") - break if char.zero? - buffer << char - offset += 1 - end - buffer.pack("#{FORMAT}*").force_encoding(ENCODING).encode("utf-8") - end - end - end -end diff --git a/lib/hid_api/wide_string.rb b/lib/hid_api/wide_string.rb index d7739b8..b8c0016 100644 --- a/lib/hid_api/wide_string.rb +++ b/lib/hid_api/wide_string.rb @@ -1,4 +1,5 @@ require "ffi" +require 'ffi_wide_char' module HidApi # An FFI data converter that marshalls data from specific device fields via @@ -10,7 +11,11 @@ class WideString class << self def from_native(value, _context) return nil if value.null? - value.read_wchar_string + FfiWideChar.read_wide_string value + end + + def to_native(value, _ctx) + FfiWideChar.to_wide_string value end end end From a6147baceef520f3e22ef4402c14f2a7e1b86790 Mon Sep 17 00:00:00 2001 From: Jeremy Mickelson Date: Sun, 3 Jan 2021 01:23:55 -0800 Subject: [PATCH 2/2] re-encode wide string to utf-8 before returning --- lib/hid_api/device.rb | 2 +- lib/hid_api/util.rb | 5 ----- lib/hid_api/wide_string.rb | 6 +++--- 3 files changed, 4 insertions(+), 9 deletions(-) delete mode 100644 lib/hid_api/util.rb diff --git a/lib/hid_api/device.rb b/lib/hid_api/device.rb index 698ff54..e17e669 100644 --- a/lib/hid_api/device.rb +++ b/lib/hid_api/device.rb @@ -86,7 +86,7 @@ def clear_buffer(length) def get_buffered_string(field) buffer = clear_buffer 255 HidApi.send "hid_get_#{field}_string", self, buffer, buffer.size - FfiWideChar.read_wide_string buffer + WideString.from_native(buffer) end def with_hid_error_handling diff --git a/lib/hid_api/util.rb b/lib/hid_api/util.rb deleted file mode 100644 index 8ac764c..0000000 --- a/lib/hid_api/util.rb +++ /dev/null @@ -1,5 +0,0 @@ -module HidApi - module Util # :nodoc: - autoload :WCHAR, "hid_api/util/wchar" - end -end diff --git a/lib/hid_api/wide_string.rb b/lib/hid_api/wide_string.rb index b8c0016..7a8f148 100644 --- a/lib/hid_api/wide_string.rb +++ b/lib/hid_api/wide_string.rb @@ -9,12 +9,12 @@ class WideString native_type FFI::Type::POINTER class << self - def from_native(value, _context) + def from_native(value, _ctx = nil) return nil if value.null? - FfiWideChar.read_wide_string value + FfiWideChar.read_wide_string(value).encode('utf-8') end - def to_native(value, _ctx) + def to_native(value, _ctx = nil) FfiWideChar.to_wide_string value end end