aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/karalabe/hid
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/karalabe/hid')
-rw-r--r--vendor/github.com/karalabe/hid/.travis.yml25
-rw-r--r--vendor/github.com/karalabe/hid/LICENSE.md8
-rw-r--r--vendor/github.com/karalabe/hid/README.md53
-rw-r--r--vendor/github.com/karalabe/hid/appveyor.yml32
-rw-r--r--vendor/github.com/karalabe/hid/hid.go37
-rw-r--r--vendor/github.com/karalabe/hid/hid_disabled.go51
-rw-r--r--vendor/github.com/karalabe/hid/hid_enabled.go228
-rw-r--r--vendor/github.com/karalabe/hid/hid_test.go28
-rw-r--r--vendor/github.com/karalabe/hid/wchar.go227
-rw-r--r--vendor/github.com/karalabe/hid/wchar_test.go66
10 files changed, 755 insertions, 0 deletions
diff --git a/vendor/github.com/karalabe/hid/.travis.yml b/vendor/github.com/karalabe/hid/.travis.yml
new file mode 100644
index 0000000..18b2067
--- /dev/null
+++ b/vendor/github.com/karalabe/hid/.travis.yml
@@ -0,0 +1,25 @@
+language: go
+
+matrix:
+ include:
+ - os: linux
+ dist: trusty
+ go: 1.5.4
+ - os: linux
+ dist: trusty
+ go: 1.6.2
+ - os: linux
+ dist: trusty
+ go: 1.7.5
+ - os: linux
+ dist: trusty
+ go: 1.8
+ - os: linux
+ dist: precise
+ go: 1.8
+ - os: osx
+ go: 1.8
+
+script:
+ - go install ./...
+ - go test -v ./...
diff --git a/vendor/github.com/karalabe/hid/LICENSE.md b/vendor/github.com/karalabe/hid/LICENSE.md
new file mode 100644
index 0000000..230d1da
--- /dev/null
+++ b/vendor/github.com/karalabe/hid/LICENSE.md
@@ -0,0 +1,8 @@
+The components of `hid` are licensed as such:
+
+ * `hidapi` is released under the [3-clause BSD](https://github.com/signal11/hidapi/blob/master/LICENSE-bsd.txt) license.
+ * `libusb` is released under the [GNU LGPL 2.1](https://github.com/libusb/libusb/blob/master/COPYING)license.
+ * `go.hid` is released under the [2-clause BSD](https://github.com/GeertJohan/go.hid/blob/master/LICENSE) license.
+ * `gowchar` is released under the [3-clause BSD](https://github.com/orofarne/gowchar/blob/master/LICENSE) license.
+
+Given the above, `hid` is licensed under GNU LGPL 2.1 or later on Linux and 3-clause BSD on other platforms.
diff --git a/vendor/github.com/karalabe/hid/README.md b/vendor/github.com/karalabe/hid/README.md
new file mode 100644
index 0000000..2851ffe
--- /dev/null
+++ b/vendor/github.com/karalabe/hid/README.md
@@ -0,0 +1,53 @@
+[![Travis][travisimg]][travisurl]
+[![AppVeyor][appveyorimg]][appveyorurl]
+[![GoDoc][docimg]][docurl]
+
+[travisimg]: https://travis-ci.org/karalabe/hid.svg?branch=master
+[travisurl]: https://travis-ci.org/karalabe/hid
+[appveyorimg]: https://ci.appveyor.com/api/projects/status/plroy54odykb0ch3/branch/master?svg=true
+[appveyorurl]: https://ci.appveyor.com/project/karalabe/hid
+[docimg]: https://godoc.org/github.com/karalabe/hid?status.svg
+[docurl]: https://godoc.org/github.com/karalabe/hid
+
+# Gopher Interface Devices (USB HID)
+
+The `hid` package is a cross platform library for accessing and communicating with USB Human Interface
+Devices (HID). It is an alternative package to [`gousb`](https://github.com/karalabe/gousb) for use
+cases where devices support this ligher mode of operation (e.g. input devices, hardware crypto wallets).
+
+The package wraps [`hidapi`](https://github.com/signal11/hidapi) for accessing OS specific USB HID APIs
+directly instead of using low level USB constructs, which might have permission issues on some platforms.
+On Linux the package also wraps [`libusb`](https://github.com/libusb/libusb). Both of these dependencies
+are vendored directly into the repository and wrapped using CGO, making the `hid` package self-contained
+and go-gettable.
+
+Supported platforms at the moment are Linux, macOS and Windows (exclude constraints are also specified
+for Android and iOS to allow smoother vendoring into cross platform projects).
+
+## Cross-compiling
+
+Using `go get` the embedded C library is compiled into the binary format of your host OS. Cross compiling to a different platform or architecture entails disabling CGO by default in Go, causing device enumeration `hid.Enumerate()` to yield no results.
+
+To cross compile a functional version of this library, you'll need to enable CGO during cross compilation via `CGO_ENABLED=1` and you'll need to install and set a cross compilation enabled C toolkit via `CC=your-cross-gcc`.
+
+## Acknowledgements
+
+Although the `hid` package is an implementation from scratch, it was heavily inspired by the existing
+[`go.hid`](https://github.com/GeertJohan/go.hid) library, which seems abandoned since 2015; is incompatible
+with Go 1.6+; and has various external dependencies. Given its inspirational roots, I thought it important
+to give credit to the author of said package too.
+
+Wide character support in the `hid` package is done via the [`gowchar`](https://github.com/orofarne/gowchar)
+library, unmaintained since 2013; non buildable with a modern Go release and failing `go vet` checks. As
+such, `gowchar` was also vendored in inline (copyright headers and origins preserved).
+
+## License
+
+The components of `hid` are licensed as such:
+
+ * `hidapi` is released under the [3-clause BSD](https://github.com/signal11/hidapi/blob/master/LICENSE-bsd.txt) license.
+ * `libusb` is released under the [GNU LGPL 2.1](https://github.com/libusb/libusb/blob/master/COPYING)license.
+ * `go.hid` is released under the [2-clause BSD](https://github.com/GeertJohan/go.hid/blob/master/LICENSE) license.
+ * `gowchar` is released under the [3-clause BSD](https://github.com/orofarne/gowchar/blob/master/LICENSE) license.
+
+Given the above, `hid` is licensed under GNU LGPL 2.1 or later on Linux and 3-clause BSD on other platforms.
diff --git a/vendor/github.com/karalabe/hid/appveyor.yml b/vendor/github.com/karalabe/hid/appveyor.yml
new file mode 100644
index 0000000..f439587
--- /dev/null
+++ b/vendor/github.com/karalabe/hid/appveyor.yml
@@ -0,0 +1,32 @@
+os: Visual Studio 2015
+
+# Clone directly into GOPATH.
+clone_folder: C:\gopath\src\github.com\karalabe\hid
+clone_depth: 1
+version: "{branch}.{build}"
+environment:
+ global:
+ GOPATH: C:\gopath
+ CC: gcc.exe
+ matrix:
+ - GOARCH: amd64
+ MSYS2_ARCH: x86_64
+ MSYS2_BITS: 64
+ MSYSTEM: MINGW64
+ PATH: C:\msys64\mingw64\bin\;%PATH%
+ - GOARCH: 386
+ MSYS2_ARCH: i686
+ MSYS2_BITS: 32
+ MSYSTEM: MINGW32
+ PATH: C:\msys64\mingw32\bin\;%PATH%
+
+install:
+ - rmdir C:\go /s /q
+ - appveyor DownloadFile https://storage.googleapis.com/golang/go1.8.windows-%GOARCH%.zip
+ - 7z x go1.8.windows-%GOARCH%.zip -y -oC:\ > NUL
+ - go version
+ - gcc --version
+
+build_script:
+ - go install ./...
+ - go test -v ./...
diff --git a/vendor/github.com/karalabe/hid/hid.go b/vendor/github.com/karalabe/hid/hid.go
new file mode 100644
index 0000000..60a40b8
--- /dev/null
+++ b/vendor/github.com/karalabe/hid/hid.go
@@ -0,0 +1,37 @@
+// hid - Gopher Interface Devices (USB HID)
+// Copyright (c) 2017 Péter Szilágyi. All rights reserved.
+//
+// This file is released under the 3-clause BSD license. Note however that Linux
+// support depends on libusb, released under GNU LGPL 2.1 or later.
+
+// Package hid provides an interface for USB HID devices.
+package hid
+
+import "errors"
+
+// ErrDeviceClosed is returned for operations where the device closed before or
+// during the execution.
+var ErrDeviceClosed = errors.New("hid: device closed")
+
+// ErrUnsupportedPlatform is returned for all operations where the underlying
+// operating system is not supported by the library.
+var ErrUnsupportedPlatform = errors.New("hid: unsupported platform")
+
+// DeviceInfo is a hidapi info structure.
+type DeviceInfo struct {
+ Path string // Platform-specific device path
+ VendorID uint16 // Device Vendor ID
+ ProductID uint16 // Device Product ID
+ Release uint16 // Device Release Number in binary-coded decimal, also known as Device Version Number
+ Serial string // Serial Number
+ Manufacturer string // Manufacturer String
+ Product string // Product string
+ UsagePage uint16 // Usage Page for this Device/Interface (Windows/Mac only)
+ Usage uint16 // Usage for this Device/Interface (Windows/Mac only)
+
+ // The USB interface which this logical device
+ // represents. Valid on both Linux implementations
+ // in all cases, and valid on the Windows implementation
+ // only if the device contains more than one interface.
+ Interface int
+}
diff --git a/vendor/github.com/karalabe/hid/hid_disabled.go b/vendor/github.com/karalabe/hid/hid_disabled.go
new file mode 100644
index 0000000..1f40263
--- /dev/null
+++ b/vendor/github.com/karalabe/hid/hid_disabled.go
@@ -0,0 +1,51 @@
+// hid - Gopher Interface Devices (USB HID)
+// Copyright (c) 2017 Péter Szilágyi. All rights reserved.
+//
+// This file is released under the 3-clause BSD license. Note however that Linux
+// support depends on libusb, released under GNU LGPL 2.1 or later.
+
+// +build !linux,!darwin,!windows ios !cgo
+
+package hid
+
+// Supported returns whether this platform is supported by the HID library or not.
+// The goal of this method is to allow programatically handling platforms that do
+// not support USB HID and not having to fall back to build constraints.
+func Supported() bool {
+ return false
+}
+
+// Enumerate returns a list of all the HID devices attached to the system which
+// match the vendor and product id. On platforms that this file implements the
+// function is a noop and returns an empty list always.
+func Enumerate(vendorID uint16, productID uint16) []DeviceInfo {
+ return nil
+}
+
+// Device is a live HID USB connected device handle. On platforms that this file
+// implements the type lacks the actual HID device and all methods are noop.
+type Device struct {
+ DeviceInfo // Embed the infos for easier access
+}
+
+// Open connects to an HID device by its path name. On platforms that this file
+// implements the method just returns an error.
+func (info DeviceInfo) Open() (*Device, error) {
+ return nil, ErrUnsupportedPlatform
+}
+
+// Close releases the HID USB device handle. On platforms that this file implements
+// the method is just a noop.
+func (dev *Device) Close() {}
+
+// Write sends an output report to a HID device. On platforms that this file
+// implements the method just returns an error.
+func (dev *Device) Write(b []byte) (int, error) {
+ return 0, ErrUnsupportedPlatform
+}
+
+// Read retrieves an input report from a HID device. On platforms that this file
+// implements the method just returns an error.
+func (dev *Device) Read(b []byte) (int, error) {
+ return 0, ErrUnsupportedPlatform
+}
diff --git a/vendor/github.com/karalabe/hid/hid_enabled.go b/vendor/github.com/karalabe/hid/hid_enabled.go
new file mode 100644
index 0000000..419273b
--- /dev/null
+++ b/vendor/github.com/karalabe/hid/hid_enabled.go
@@ -0,0 +1,228 @@
+// hid - Gopher Interface Devices (USB HID)
+// Copyright (c) 2017 Péter Szilágyi. All rights reserved.
+//
+// This file is released under the 3-clause BSD license. Note however that Linux
+// support depends on libusb, released under LGNU GPL 2.1 or later.
+
+// +build linux,cgo darwin,!ios,cgo windows,cgo
+
+package hid
+
+/*
+#cgo CFLAGS: -I./hidapi/hidapi
+
+#cgo linux CFLAGS: -I./libusb/libusb -DDEFAULT_VISIBILITY="" -DOS_LINUX -D_GNU_SOURCE -DPOLL_NFDS_TYPE=int
+#cgo linux,!android LDFLAGS: -lrt
+#cgo darwin CFLAGS: -DOS_DARWIN
+#cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit
+#cgo windows CFLAGS: -DOS_WINDOWS
+#cgo windows LDFLAGS: -lsetupapi
+
+#ifdef OS_LINUX
+ #include <sys/poll.h>
+ #include "os/threads_posix.c"
+ #include "os/poll_posix.c"
+
+ #include "os/linux_usbfs.c"
+ #include "os/linux_netlink.c"
+
+ #include "core.c"
+ #include "descriptor.c"
+ #include "hotplug.c"
+ #include "io.c"
+ #include "strerror.c"
+ #include "sync.c"
+
+ #include "hidapi/libusb/hid.c"
+#elif OS_DARWIN
+ #include "hidapi/mac/hid.c"
+#elif OS_WINDOWS
+ #include "hidapi/windows/hid.c"
+#endif
+*/
+import "C"
+import (
+ "errors"
+ "runtime"
+ "sync"
+ "unsafe"
+)
+
+// enumerateLock is a mutex serializing access to USB device enumeration needed
+// by the macOS USB HID system calls, which require 2 consecutive method calls
+// for enumeration, causing crashes if called concurrently.
+//
+// For more details, see:
+// https://developer.apple.com/documentation/iokit/1438371-iohidmanagersetdevicematching
+// > "subsequent calls will cause the hid manager to release previously enumerated devices"
+var enumerateLock sync.Mutex
+
+func init() {
+ // Initialize the HIDAPI library
+ C.hid_init()
+}
+
+// Supported returns whether this platform is supported by the HID library or not.
+// The goal of this method is to allow programatically handling platforms that do
+// not support USB HID and not having to fall back to build constraints.
+func Supported() bool {
+ return true
+}
+
+// Enumerate returns a list of all the HID devices attached to the system which
+// match the vendor and product id:
+// - If the vendor id is set to 0 then any vendor matches.
+// - If the product id is set to 0 then any product matches.
+// - If the vendor and product id are both 0, all HID devices are returned.
+func Enumerate(vendorID uint16, productID uint16) []DeviceInfo {
+ enumerateLock.Lock()
+ defer enumerateLock.Unlock()
+
+ // Gather all device infos and ensure they are freed before returning
+ head := C.hid_enumerate(C.ushort(vendorID), C.ushort(productID))
+ if head == nil {
+ return nil
+ }
+ defer C.hid_free_enumeration(head)
+
+ // Iterate the list and retrieve the device details
+ var infos []DeviceInfo
+ for ; head != nil; head = head.next {
+ info := DeviceInfo{
+ Path: C.GoString(head.path),
+ VendorID: uint16(head.vendor_id),
+ ProductID: uint16(head.product_id),
+ Release: uint16(head.release_number),
+ UsagePage: uint16(head.usage_page),
+ Usage: uint16(head.usage),
+ Interface: int(head.interface_number),
+ }
+ if head.serial_number != nil {
+ info.Serial, _ = wcharTToString(head.serial_number)
+ }
+ if head.product_string != nil {
+ info.Product, _ = wcharTToString(head.product_string)
+ }
+ if head.manufacturer_string != nil {
+ info.Manufacturer, _ = wcharTToString(head.manufacturer_string)
+ }
+ infos = append(infos, info)
+ }
+ return infos
+}
+
+// Open connects to an HID device by its path name.
+func (info DeviceInfo) Open() (*Device, error) {
+ path := C.CString(info.Path)
+ defer C.free(unsafe.Pointer(path))
+
+ device := C.hid_open_path(path)
+ if device == nil {
+ return nil, errors.New("hidapi: failed to open device")
+ }
+ return &Device{
+ DeviceInfo: info,
+ device: device,
+ }, nil
+}
+
+// Device is a live HID USB connected device handle.
+type Device struct {
+ DeviceInfo // Embed the infos for easier access
+
+ device *C.hid_device // Low level HID device to communicate through
+ lock sync.Mutex
+}
+
+// Close releases the HID USB device handle.
+func (dev *Device) Close() {
+ dev.lock.Lock()
+ defer dev.lock.Unlock()
+
+ if dev.device != nil {
+ C.hid_close(dev.device)
+ dev.device = nil
+ }
+}
+
+// Write sends an output report to a HID device.
+//
+// Write will send the data on the first OUT endpoint, if one exists. If it does
+// not, it will send the data through the Control Endpoint (Endpoint 0).
+func (dev *Device) Write(b []byte) (int, error) {
+ // Abort if nothing to write
+ if len(b) == 0 {
+ return 0, nil
+ }
+ // Abort if device closed in between
+ dev.lock.Lock()
+ device := dev.device
+ dev.lock.Unlock()
+
+ if device == nil {
+ return 0, ErrDeviceClosed
+ }
+ // Prepend a HID report ID on Windows, other OSes don't need it
+ var report []byte
+ if runtime.GOOS == "windows" {
+ report = append([]byte{0x00}, b...)
+ } else {
+ report = b
+ }
+ // Execute the write operation
+ written := int(C.hid_write(device, (*C.uchar)(&report[0]), C.size_t(len(report))))
+ if written == -1 {
+ // If the write failed, verify if closed or other error
+ dev.lock.Lock()
+ device = dev.device
+ dev.lock.Unlock()
+
+ if device == nil {
+ return 0, ErrDeviceClosed
+ }
+ // Device not closed, some other error occurred
+ message := C.hid_error(device)
+ if message == nil {
+ return 0, errors.New("hidapi: unknown failure")
+ }
+ failure, _ := wcharTToString(message)
+ return 0, errors.New("hidapi: " + failure)
+ }
+ return written, nil
+}
+
+// Read retrieves an input report from a HID device.
+func (dev *Device) Read(b []byte) (int, error) {
+ // Aborth if nothing to read
+ if len(b) == 0 {
+ return 0, nil
+ }
+ // Abort if device closed in between
+ dev.lock.Lock()
+ device := dev.device
+ dev.lock.Unlock()
+
+ if device == nil {
+ return 0, ErrDeviceClosed
+ }
+ // Execute the read operation
+ read := int(C.hid_read(device, (*C.uchar)(&b[0]), C.size_t(len(b))))
+ if read == -1 {
+ // If the read failed, verify if closed or other error
+ dev.lock.Lock()
+ device = dev.device
+ dev.lock.Unlock()
+
+ if device == nil {
+ return 0, ErrDeviceClosed
+ }
+ // Device not closed, some other error occurred
+ message := C.hid_error(device)
+ if message == nil {
+ return 0, errors.New("hidapi: unknown failure")
+ }
+ failure, _ := wcharTToString(message)
+ return 0, errors.New("hidapi: " + failure)
+ }
+ return read, nil
+}
diff --git a/vendor/github.com/karalabe/hid/hid_test.go b/vendor/github.com/karalabe/hid/hid_test.go
new file mode 100644
index 0000000..470e6b9
--- /dev/null
+++ b/vendor/github.com/karalabe/hid/hid_test.go
@@ -0,0 +1,28 @@
+// hid - Gopher Interface Devices (USB HID)
+// Copyright (c) 2017 Péter Szilágyi. All rights reserved.
+//
+// This file is released under the 3-clause BSD license. Note however that Linux
+// support depends on libusb, released under GNU LGPL 2.1 or later.
+
+package hid
+
+import (
+ "sync"
+ "testing"
+)
+
+// Tests that device enumeration can be called concurrently from multiple threads.
+func TestThreadedEnumerate(t *testing.T) {
+ var pend sync.WaitGroup
+ for i := 0; i < 8; i++ {
+ pend.Add(1)
+
+ go func(index int) {
+ defer pend.Done()
+ for j := 0; j < 512; j++ {
+ Enumerate(uint16(index), 0)
+ }
+ }(i)
+ }
+ pend.Wait()
+}
diff --git a/vendor/github.com/karalabe/hid/wchar.go b/vendor/github.com/karalabe/hid/wchar.go
new file mode 100644
index 0000000..d103bef
--- /dev/null
+++ b/vendor/github.com/karalabe/hid/wchar.go
@@ -0,0 +1,227 @@
+// This file is https://github.com/orofarne/gowchar/blob/master/gowchar.go
+//
+// It was vendored inline to work around CGO limitations that don't allow C types
+// to directly cross package API boundaries.
+//
+// The vendored file is licensed under the 3-clause BSD license, according to:
+// https://github.com/orofarne/gowchar/blob/master/LICENSE
+
+// +build !ios
+// +build linux darwin windows
+
+package hid
+
+/*
+#include <wchar.h>
+
+const size_t SIZEOF_WCHAR_T = sizeof(wchar_t);
+
+void gowchar_set (wchar_t *arr, int pos, wchar_t val)
+{
+ arr[pos] = val;
+}
+
+wchar_t gowchar_get (wchar_t *arr, int pos)
+{
+ return arr[pos];
+}
+*/
+import "C"
+
+import (
+ "fmt"
+ "unicode/utf16"
+ "unicode/utf8"
+)
+
+var sizeofWcharT C.size_t = C.size_t(C.SIZEOF_WCHAR_T)
+
+func stringToWcharT(s string) (*C.wchar_t, C.size_t) {
+ switch sizeofWcharT {
+ case 2:
+ return stringToWchar2(s) // Windows
+ case 4:
+ return stringToWchar4(s) // Unix
+ default:
+ panic(fmt.Sprintf("Invalid sizeof(wchar_t) = %v", sizeofWcharT))
+ }
+}
+
+func wcharTToString(s *C.wchar_t) (string, error) {
+ switch sizeofWcharT {
+ case 2:
+ return wchar2ToString(s) // Windows
+ case 4:
+ return wchar4ToString(s) // Unix
+ default:
+ panic(fmt.Sprintf("Invalid sizeof(wchar_t) = %v", sizeofWcharT))
+ }
+}
+
+func wcharTNToString(s *C.wchar_t, size C.size_t) (string, error) {
+ switch sizeofWcharT {
+ case 2:
+ return wchar2NToString(s, size) // Windows
+ case 4:
+ return wchar4NToString(s, size) // Unix
+ default:
+ panic(fmt.Sprintf("Invalid sizeof(wchar_t) = %v", sizeofWcharT))
+ }
+}
+
+// Windows
+func stringToWchar2(s string) (*C.wchar_t, C.size_t) {
+ var slen int
+ s1 := s
+ for len(s1) > 0 {
+ r, size := utf8.DecodeRuneInString(s1)
+ if er, _ := utf16.EncodeRune(r); er == '\uFFFD' {
+ slen += 1
+ } else {
+ slen += 2
+ }
+ s1 = s1[size:]
+ }
+ slen++ // \0
+ res := C.malloc(C.size_t(slen) * sizeofWcharT)
+ var i int
+ for len(s) > 0 {
+ r, size := utf8.DecodeRuneInString(s)
+ if r1, r2 := utf16.EncodeRune(r); r1 != '\uFFFD' {
+ C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r1))
+ i++
+ C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r2))
+ i++
+ } else {
+ C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r))
+ i++
+ }
+ s = s[size:]
+ }
+ C.gowchar_set((*C.wchar_t)(res), C.int(slen-1), C.wchar_t(0)) // \0
+ return (*C.wchar_t)(res), C.size_t(slen)
+}
+
+// Unix
+func stringToWchar4(s string) (*C.wchar_t, C.size_t) {
+ slen := utf8.RuneCountInString(s)
+ slen++ // \0
+ res := C.malloc(C.size_t(slen) * sizeofWcharT)
+ var i int
+ for len(s) > 0 {
+ r, size := utf8.DecodeRuneInString(s)
+ C.gowchar_set((*C.wchar_t)(res), C.int(i), C.wchar_t(r))
+ s = s[size:]
+ i++
+ }
+ C.gowchar_set((*C.wchar_t)(res), C.int(slen-1), C.wchar_t(0)) // \0
+ return (*C.wchar_t)(res), C.size_t(slen)
+}
+
+// Windows
+func wchar2ToString(s *C.wchar_t) (string, error) {
+ var i int
+ var res string
+ for {
+ ch := C.gowchar_get(s, C.int(i))
+ if ch == 0 {
+ break
+ }
+ r := rune(ch)
+ i++
+ if !utf16.IsSurrogate(r) {
+ if !utf8.ValidRune(r) {
+ err := fmt.Errorf("Invalid rune at position %v", i)
+ return "", err
+ }
+ res += string(r)
+ } else {
+ ch2 := C.gowchar_get(s, C.int(i))
+ r2 := rune(ch2)
+ r12 := utf16.DecodeRune(r, r2)
+ if r12 == '\uFFFD' {
+ err := fmt.Errorf("Invalid surrogate pair at position %v", i-1)
+ return "", err
+ }
+ res += string(r12)
+ i++
+ }
+ }
+ return res, nil
+}
+
+// Unix
+func wchar4ToString(s *C.wchar_t) (string, error) {
+ var i int
+ var res string
+ for {
+ ch := C.gowchar_get(s, C.int(i))
+ if ch == 0 {
+ break
+ }
+ r := rune(ch)
+ if !utf8.ValidRune(r) {
+ err := fmt.Errorf("Invalid rune at position %v", i)
+ return "", err
+ }
+ res += string(r)
+ i++
+ }
+ return res, nil
+}
+
+// Windows
+func wchar2NToString(s *C.wchar_t, size C.size_t) (string, error) {
+ var i int
+ var res string
+ N := int(size)
+ for i < N {
+ ch := C.gowchar_get(s, C.int(i))
+ if ch == 0 {
+ break
+ }
+ r := rune(ch)
+ i++
+ if !utf16.IsSurrogate(r) {
+ if !utf8.ValidRune(r) {
+ err := fmt.Errorf("Invalid rune at position %v", i)
+ return "", err
+ }
+
+ res += string(r)
+ } else {
+ if i >= N {
+ err := fmt.Errorf("Invalid surrogate pair at position %v", i-1)
+ return "", err
+ }
+ ch2 := C.gowchar_get(s, C.int(i))
+ r2 := rune(ch2)
+ r12 := utf16.DecodeRune(r, r2)
+ if r12 == '\uFFFD' {
+ err := fmt.Errorf("Invalid surrogate pair at position %v", i-1)
+ return "", err
+ }
+ res += string(r12)
+ i++
+ }
+ }
+ return res, nil
+}
+
+// Unix
+func wchar4NToString(s *C.wchar_t, size C.size_t) (string, error) {
+ var i int
+ var res string
+ N := int(size)
+ for i < N {
+ ch := C.gowchar_get(s, C.int(i))
+ r := rune(ch)
+ if !utf8.ValidRune(r) {
+ err := fmt.Errorf("Invalid rune at position %v", i)
+ return "", err
+ }
+ res += string(r)
+ i++
+ }
+ return res, nil
+}
diff --git a/vendor/github.com/karalabe/hid/wchar_test.go b/vendor/github.com/karalabe/hid/wchar_test.go
new file mode 100644
index 0000000..b80ebb8
--- /dev/null
+++ b/vendor/github.com/karalabe/hid/wchar_test.go
@@ -0,0 +1,66 @@
+// This file is https://github.com/orofarne/gowchar/blob/master/gowchar_test.go
+//
+// It was vendored inline to work around CGO limitations that don't allow C types
+// to directly cross package API boundaries.
+//
+// The vendored file is licensed under the 3-clause BSD license, according to:
+// https://github.com/orofarne/gowchar/blob/master/LICENSE
+
+// +build !ios
+// +build linux darwin windows
+
+package hid
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestGowcharSimple(t *testing.T) {
+ str1 := "Привет, 世界. 𪛖"
+ wstr, size := stringToWcharT(str1)
+ switch sizeofWcharT {
+ case 2:
+ if size != 15 {
+ t.Errorf("size(%v) != 15", size)
+ }
+ case 4:
+ if size != 14 {
+ t.Errorf("size(%v) != 14", size)
+ }
+ default:
+ panic(fmt.Sprintf("sizeof(wchar_t) = %v", sizeofWcharT))
+ }
+ str2, err := wcharTToString(wstr)
+ if err != nil {
+ t.Errorf("wcharTToString error: %v", err)
+ }
+ if str1 != str2 {
+ t.Errorf("\"%s\" != \"%s\"", str1, str2)
+ }
+}
+
+func TestGowcharSimpleN(t *testing.T) {
+ str1 := "Привет, 世界. 𪛖"
+ wstr, size := stringToWcharT(str1)
+ switch sizeofWcharT {
+ case 2:
+ if size != 15 {
+ t.Errorf("size(%v) != 15", size)
+ }
+ case 4:
+ if size != 14 {
+ t.Errorf("size(%v) != 14", size)
+ }
+ default:
+ panic(fmt.Sprintf("sizeof(wchar_t) = %v", sizeofWcharT))
+ }
+
+ str2, err := wcharTNToString(wstr, size-1)
+ if err != nil {
+ t.Errorf("wcharTToString error: %v", err)
+ }
+ if str1 != str2 {
+ t.Errorf("\"%s\" != \"%s\"", str1, str2)
+ }
+}