hidaping 1.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c38b9b9d15828de26fea00975aa9744b1ab4351cc52bba779c3daf257b987efc
4
+ data.tar.gz: d65edf49a8fe4b072f3e68ad038d31d4a349c4a4331f2f38d24cfc20157780c4
5
+ SHA512:
6
+ metadata.gz: 7212e791d8fcc018e825b6ed54a2add5c8c2096e7aea2b8ce9375379a3710f25d8268d973e67b1fa691d928c5cd42e878580f40745fbeeac39890b07ddc8f5ea
7
+ data.tar.gz: c8a7bf89640c6962fed10d48747cc3d024bc285889afe83fdfe2ea1221dd053265a4fdc70ba9b863eed961172cf557f2cb4e84e4ee7c900cb4b73e56b3076d90
data/.clang-format ADDED
@@ -0,0 +1,5 @@
1
+ BasedOnStyle: LLVM
2
+
3
+ ColumnLimit: 100
4
+ IndentWidth: 4
5
+
@@ -0,0 +1,50 @@
1
+ name: Ruby Gem
2
+
3
+ on:
4
+ push:
5
+ branches: [ "master" ]
6
+ pull_request:
7
+ branches: [ "master" ]
8
+
9
+ jobs:
10
+ build:
11
+ name: Build + Publish
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: read
15
+ pages: write
16
+ id-token: write
17
+
18
+ steps:
19
+ - name: Clone
20
+ uses: actions/checkout@v4
21
+ - name: Set up Ruby 3.4.5
22
+ uses: ruby/setup-ruby@v1
23
+ with:
24
+ ruby-version: 3.4.5
25
+ - name: Build
26
+ run: |
27
+ bundle install
28
+ bundle exec rake gem
29
+
30
+ mkdir -p ~/.gem
31
+ cat <<AAA > ~/.gem/.mirrorrc
32
+ ---
33
+ - from: https://repy.github.io/hidaping
34
+ to: /home/runner/gemrepos
35
+ AAA
36
+ mkdir -p /home/runner/gemrepos/gems
37
+ gem install rubygems-mirror
38
+ gem mirror
39
+
40
+ cp ./pkg/hidaping-*.gem /home/runner/gemrepos/gems/
41
+ cd /home/runner/gemrepos
42
+ gem generate_index
43
+ - name: Setup Pages
44
+ uses: actions/configure-pages@v5
45
+ - name: Upload artifact
46
+ uses: actions/upload-pages-artifact@v3
47
+ with:
48
+ path: /home/runner/gemrepos
49
+ - name: Deploy to GitHub Pages
50
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,53 @@
1
+ name: Test on All OS
2
+
3
+ on:
4
+ push:
5
+ branches: ["master"]
6
+ pull_request:
7
+ branches: ["master"]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ matrix:
14
+ os:
15
+ - ubuntu-latest
16
+ - windows-latest
17
+ - macos-latest
18
+
19
+ steps:
20
+ - name: Clone
21
+ uses: actions/checkout@v4
22
+ - name: Set up Ruby 3.4.5
23
+ uses: ruby/setup-ruby@v1
24
+ with:
25
+ ruby-version: 3.4.5
26
+
27
+ - name: Run tests (Ubuntu)
28
+ if: runner.os == 'Linux'
29
+ run: |
30
+ sudo apt-get update
31
+ sudo apt install -y libhidapi-dev
32
+ gem install bundler
33
+ bundle install
34
+ bundle exec rake compile
35
+ bundle exec ruby test/test.rb
36
+
37
+ - name: Run tests (Windows)
38
+ if: runner.os == 'Windows'
39
+ run: |
40
+ pacman --noconfirm -S mingw-w64-ucrt-x86_64-hidapi
41
+ gem install bundler
42
+ bundle install
43
+ bundle exec rake compile
44
+ bundle exec ruby test/test.rb
45
+
46
+ - name: Run tests (macOS)
47
+ if: runner.os == 'macOS'
48
+ run: |
49
+ brew install hidapi
50
+ gem install bundler
51
+ bundle install
52
+ bundle exec rake compile
53
+ sudo bundle exec ruby test/test.rb
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.4.5
data/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ === 1.1.0 / 2024-05-24
2
+
3
+ * Forked from https://github.com/tenderlove/myhidapi and updated project metadata.
4
+ * Improved the build process to use `pkg-config` for locating the `hidapi` library. This simplifies installation on systems where `hidapi` is installed via a package manager (e.g., Homebrew, apt).
5
+
6
+ === 1.0.0 / 2019-02-15
7
+
8
+ * 1 major enhancement
9
+ * Birthday!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Repy
4
+ Copyright (c) 2019 Aaron Patterson
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the “Software”), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # HIDAPING
2
+
3
+ Next generation HIDAPI wrapper for Ruby
4
+
5
+ ## DESCRIPTION
6
+
7
+ This is a small wrapper around HIDAPI.
8
+ I couldn't get other HIDAPI wrappers to work, so I wrote this one.
9
+ I'm using it to communicate with my keyboard, so it really only supports enough of the HIDAPI to do that.
10
+
11
+ ## DESCRIPTION
12
+
13
+ * https://github.com/Repy/hidaping
14
+
15
+ This gem is a fork from the original work by Aaron Patterson ([@tenderlove](https://github.com/tenderlove)).
16
+
17
+ * Original Repository: https://github.com/tenderlove/myhidapi
18
+
19
+ ## SUPPORTS
20
+
21
+ This gem works on the following platforms, leveraging the capabilities of the underlying HIDAPI library.
22
+
23
+ * **Platforms**:
24
+ * Windows
25
+ * macOS
26
+ * Linux
27
+
28
+ * **Device Types**:
29
+ * USB
30
+ * Bluetooth
31
+
32
+ ## FEATURES/PROBLEMS
33
+
34
+ * Incomplete
35
+ * No tests
36
+ * Seems to work (for me)
37
+
38
+ ## SYNOPSIS
39
+
40
+ ```ruby
41
+ require 'hidaping'
42
+
43
+ devices = HIDAPING.enumerate(0x0, 0x0)
44
+
45
+ for d in devices
46
+ printf("0x%04x, 0x%04x, %s\n", d.vendor_id, d.product_id, d.product_string)
47
+ end
48
+
49
+ handle = HIDAPING.open(0x045e, 0x028e)
50
+
51
+ while true
52
+ report_raw = handle.read_timeout(64, 500)
53
+ data = report_raw.unpack("vvvvvb*")
54
+ p data
55
+ end
56
+ ```
57
+
58
+ ## REQUIREMENTS
59
+
60
+ This depends on **hidapi**.
61
+
62
+ ## INSTALL
63
+
64
+ First, install the **hidapi** library. Here are instructions for common platforms:
65
+
66
+ * **Windows (using RubyInstaller with Devkit)**
67
+
68
+ ```sh
69
+ ridk exec pacman -S mingw-w64-ucrt-x86_64-hidapi
70
+ ```
71
+
72
+ * **macOS (using Homebrew)**
73
+
74
+ ```sh
75
+ brew install hidapi
76
+ ```
77
+
78
+ * **Ubuntu / Debian**
79
+
80
+ ```sh
81
+ sudo apt update
82
+ sudo apt install libhidapi-dev
83
+ ```
84
+
85
+ Once `hidapi` is installed, you can install this gem:
86
+
87
+ ```sh
88
+ gem install hidaping
89
+ ```
90
+
91
+ ## LICENSE
92
+
93
+ The gem is available as open source under the terms of the [MIT License](LICENSE.md).
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/extensiontask"
5
+ require 'rake/packagetask'
6
+
7
+ task build: :compile
8
+
9
+ GEMSPEC = Gem::Specification.load("hidaping.gemspec")
10
+
11
+ Rake::ExtensionTask.new("hidaping", GEMSPEC) do |ext|
12
+ ext.lib_dir = "lib/hidaping"
13
+ end
14
+
15
+ Gem::PackageTask.new(GEMSPEC) do |pkg|
16
+ end
17
+
18
+ task default: %i[clobber compile]
@@ -0,0 +1,17 @@
1
+ #ifndef MY_HIDAPI_COMMON_H
2
+ #define MY_HIDAPI_COMMON_H
3
+
4
+ #include "extconf.h"
5
+
6
+ #if defined(HAVE_HIDAPI_H)
7
+ #include <hidapi.h>
8
+ #elif defined(HAVE_HIDAPI_HIDAPI_H)
9
+ #include <hidapi/hidapi.h>
10
+ #else
11
+ #error "No suitable header found"
12
+ #endif
13
+
14
+ #include <ruby.h>
15
+ #include <stdlib.h>
16
+
17
+ #endif // MY_HIDAPI_COMMON_H
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ dir_config("hidapi")
6
+ pkg_config('hidapi')
7
+
8
+ if have_library('hidapi')
9
+ libname = 'hidapi'
10
+ elsif have_library('hidapi-hidraw')
11
+ libname = 'hidapi-hidraw'
12
+ elsif have_library('hidapi-libusb')
13
+ libname = 'hidapi-libusb'
14
+ else
15
+ abort 'you must have "libhidapi".'
16
+ end
17
+
18
+ if have_header('hidapi/hidapi.h')
19
+ header = 'hidapi/hidapi.h'
20
+ elsif have_header('hidapi.h')
21
+ header = 'hidapi.h'
22
+ else
23
+ abort 'you must have "hidapi.h".'
24
+ end
25
+
26
+ create_header
27
+ create_makefile("hidaping/hidaping")
@@ -0,0 +1,57 @@
1
+ #include "hidaping.h"
2
+ #include "hidaping_device.h"
3
+ #include "hidaping_util.h"
4
+
5
+ VALUE mHIDAPING;
6
+
7
+ static VALUE enumerate(VALUE mod, VALUE vendor_id, VALUE product_id) {
8
+ VALUE devices;
9
+ struct hid_device_info *devs, *cur_dev;
10
+
11
+ VALUE device_info_class = rb_path2class("HIDAPING::DeviceInfo");
12
+
13
+ devices = rb_ary_new();
14
+ devs = hid_enumerate(NUM2USHORT(vendor_id), NUM2USHORT(product_id));
15
+ cur_dev = devs;
16
+ while (cur_dev) {
17
+ VALUE args[] = {INT2NUM(cur_dev->vendor_id),
18
+ INT2NUM(cur_dev->product_id),
19
+ rb_str_new2(cur_dev->path),
20
+ hidaping_wcstombs(cur_dev->serial_number),
21
+ hidaping_wcstombs(cur_dev->manufacturer_string),
22
+ hidaping_wcstombs(cur_dev->product_string),
23
+ INT2NUM(cur_dev->usage),
24
+ INT2NUM(cur_dev->interface_number)};
25
+ rb_ary_push(devices, rb_class_new_instance(8, args, device_info_class));
26
+ cur_dev = cur_dev->next;
27
+ }
28
+ hid_free_enumeration(devs);
29
+
30
+ return devices;
31
+ }
32
+
33
+ static VALUE rb_hid_open(VALUE mod, VALUE vid, VALUE pid) {
34
+ hid_device *handle = hid_open(NUM2USHORT(vid), NUM2USHORT(pid), NULL);
35
+ if (handle) {
36
+ return create_hidaping_handle(handle);
37
+ } else {
38
+ return Qfalse;
39
+ }
40
+ }
41
+
42
+ static VALUE rb_hid_open_path(VALUE mod, VALUE path) {
43
+ hid_device *handle = hid_open_path(StringValueCStr(path));
44
+ if (handle) {
45
+ return create_hidaping_handle(handle);
46
+ } else {
47
+ return Qfalse;
48
+ }
49
+ }
50
+
51
+ extern void Init_hidaping(void) {
52
+ mHIDAPING = rb_define_module("HIDAPING");
53
+ rb_define_singleton_method(mHIDAPING, "enumerate", enumerate, 2);
54
+ rb_define_singleton_method(mHIDAPING, "open", rb_hid_open, 2);
55
+ rb_define_singleton_method(mHIDAPING, "open_path", rb_hid_open_path, 1);
56
+ Init_hidaping_device(mHIDAPING);
57
+ }
@@ -0,0 +1,6 @@
1
+ #ifndef MY_HIDAPI_H
2
+ #define MY_HIDAPI_H
3
+
4
+ #include "common.h"
5
+
6
+ #endif // MY_HIDAPI_H
@@ -0,0 +1,159 @@
1
+ #include "hidaping_device.h"
2
+ #include "hidaping.h"
3
+ #include "hidaping_util.h"
4
+
5
+ #define BUF_SIZE 4096
6
+
7
+ VALUE cHIDAPINGHandle;
8
+
9
+ static void dealloc(void *ptr) {
10
+ hid_device *handle = (hid_device *)ptr;
11
+ hid_close(handle);
12
+ }
13
+
14
+ static const rb_data_type_t hidaping_handle_type = {
15
+ "HIDAPING/Handle",
16
+ {
17
+ 0,
18
+ dealloc,
19
+ 0,
20
+ },
21
+ 0,
22
+ 0,
23
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
24
+ RUBY_TYPED_FREE_IMMEDIATELY,
25
+ #endif
26
+ };
27
+
28
+ VALUE create_hidaping_handle(hid_device *handle) {
29
+ return TypedData_Wrap_Struct(cHIDAPINGHandle, &hidaping_handle_type, handle);
30
+ }
31
+
32
+ static VALUE rb_hid_write(VALUE self, VALUE str) {
33
+ hid_device *handle;
34
+ TypedData_Get_Struct(self, hid_device, &hidaping_handle_type, handle);
35
+
36
+ int written = hid_write(handle, (unsigned char *)StringValuePtr(str), RSTRING_LEN(str));
37
+
38
+ if (written >= 0) {
39
+ return INT2NUM(written);
40
+ } else {
41
+ return Qfalse;
42
+ }
43
+ }
44
+
45
+ static VALUE rb_hid_set_nonblocking(VALUE self, VALUE val) {
46
+ hid_device *handle;
47
+ TypedData_Get_Struct(self, hid_device, &hidaping_handle_type, handle);
48
+
49
+ if (!hid_set_nonblocking(handle, NUM2INT(val))) {
50
+ return Qtrue;
51
+ } else {
52
+ return Qnil;
53
+ }
54
+ }
55
+
56
+ static VALUE rb_hid_read(VALUE self, VALUE size) {
57
+ hid_device *handle;
58
+ TypedData_Get_Struct(self, hid_device, &hidaping_handle_type, handle);
59
+
60
+ unsigned char *buf = xcalloc(NUM2SIZET(size), sizeof(unsigned char));
61
+ int read = hid_read(handle, buf, NUM2SIZET(size));
62
+
63
+ VALUE ret;
64
+ if (read > 0) {
65
+ ret = rb_str_new((char *)buf, read);
66
+ } else {
67
+ ret = Qnil;
68
+ }
69
+
70
+ xfree(buf);
71
+ return ret;
72
+ }
73
+
74
+ static VALUE rb_hid_read_timeout(VALUE self, VALUE size, VALUE timeout_ms) {
75
+ hid_device *handle;
76
+ unsigned char *buf;
77
+ int read;
78
+ VALUE ret;
79
+
80
+ TypedData_Get_Struct(self, hid_device, &hidaping_handle_type, handle);
81
+
82
+ buf = xcalloc(NUM2SIZET(size), sizeof(unsigned char));
83
+
84
+ read = hid_read_timeout(handle, buf, NUM2SIZET(size), NUM2INT(timeout_ms));
85
+
86
+ if (read > 0) {
87
+ ret = rb_str_new((char *)buf, read);
88
+ } else {
89
+ ret = Qnil;
90
+ }
91
+ xfree(buf);
92
+ return ret;
93
+ }
94
+
95
+ static VALUE rb_hid_send_feature_report(VALUE self, VALUE str) {
96
+ hid_device *handle;
97
+ TypedData_Get_Struct(self, hid_device, &hidaping_handle_type, handle);
98
+
99
+ int written =
100
+ hid_send_feature_report(handle, (unsigned char *)StringValuePtr(str), RSTRING_LEN(str));
101
+
102
+ if (written >= 0) {
103
+ return INT2NUM(written);
104
+ } else {
105
+ return Qfalse;
106
+ }
107
+ }
108
+
109
+ static VALUE rb_hid_get_feature_report(VALUE self, VALUE report_id, VALUE size) {
110
+ hid_device *handle;
111
+ TypedData_Get_Struct(self, hid_device, &hidaping_handle_type, handle);
112
+
113
+ int buf_size = NUM2SIZET(size);
114
+ unsigned char *buf = xcalloc(buf_size, sizeof(unsigned char));
115
+ int read = hid_get_feature_report(handle, buf, buf_size);
116
+
117
+ VALUE ret;
118
+ if (read > 0) {
119
+ ret = rb_str_new((char *)buf, read);
120
+ } else {
121
+ ret = Qnil;
122
+ }
123
+
124
+ xfree(buf);
125
+ return ret;
126
+ }
127
+
128
+ static VALUE rb_manufacturer(VALUE self) {
129
+ hid_device *handle;
130
+ TypedData_Get_Struct(self, hid_device, &hidaping_handle_type, handle);
131
+
132
+ wchar_t buffer[BUF_SIZE];
133
+ hid_get_manufacturer_string(handle, buffer, BUF_SIZE);
134
+
135
+ return hidaping_wcstombs(buffer);
136
+ }
137
+
138
+ static VALUE rb_product(VALUE self) {
139
+ hid_device *handle;
140
+ TypedData_Get_Struct(self, hid_device, &hidaping_handle_type, handle);
141
+
142
+ wchar_t buffer[BUF_SIZE];
143
+ hid_get_product_string(handle, buffer, BUF_SIZE);
144
+
145
+ return hidaping_wcstombs(buffer);
146
+ }
147
+
148
+ void Init_hidaping_device(VALUE mHIDAPING) {
149
+ cHIDAPINGHandle = rb_define_class_under(mHIDAPING, "Handle", rb_cObject);
150
+ rb_undef_alloc_func(cHIDAPINGHandle);
151
+ rb_define_method(cHIDAPINGHandle, "write", rb_hid_write, 1);
152
+ rb_define_method(cHIDAPINGHandle, "read", rb_hid_read, 1);
153
+ rb_define_method(cHIDAPINGHandle, "read_timeout", rb_hid_read_timeout, 2);
154
+ rb_define_method(cHIDAPINGHandle, "set_nonblocking", rb_hid_set_nonblocking, 1);
155
+ rb_define_method(cHIDAPINGHandle, "send_feature_report", rb_hid_send_feature_report, 1);
156
+ rb_define_method(cHIDAPINGHandle, "get_feature_report", rb_hid_get_feature_report, 2);
157
+ rb_define_method(cHIDAPINGHandle, "manufacturer", rb_manufacturer, 0);
158
+ rb_define_method(cHIDAPINGHandle, "product", rb_product, 0);
159
+ }
@@ -0,0 +1,9 @@
1
+ #ifndef MY_HIDAPI_DEVICE_H
2
+ #define MY_HIDAPI_DEVICE_H
3
+
4
+ #include "common.h"
5
+
6
+ VALUE create_hidaping_handle(hid_device *handle);
7
+ void Init_hidaping_device(VALUE mHIDAPING);
8
+
9
+ #endif // MY_HIDAPI_DEVICE_H
@@ -0,0 +1,19 @@
1
+ #ifndef MY_HIDAPI_UTIL_H
2
+ #define MY_HIDAPI_UTIL_H
3
+
4
+ #include "common.h"
5
+
6
+ #define BUF_SIZE 4096
7
+
8
+ static VALUE hidaping_wcstombs(wchar_t *str) {
9
+ char buf[BUF_SIZE];
10
+ int len;
11
+ len = wcstombs(buf, str, BUF_SIZE);
12
+ if (len > 0) {
13
+ return rb_str_new(buf, len);
14
+ } else {
15
+ return Qnil;
16
+ }
17
+ }
18
+
19
+ #endif // MY_HIDAPI_UTIL_H
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HIDAPING
4
+ VERSION = '1.1.0'
5
+ end
data/lib/hidaping.rb ADDED
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "hidaping/hidaping.so"
4
+
5
+ module HIDAPING
6
+
7
+ class DeviceInfo
8
+ attr_reader :vendor_id, :product_id, :path, :serial_number, :manufacturer_string, :product_string, :usage, :interface_number
9
+
10
+ def initialize vendor_id, product_id, path, serial_number, manufacturer_string, product_string, usage, interface_number
11
+ @vendor_id = vendor_id
12
+ @product_id = product_id
13
+ @path = path
14
+ @serial_number = serial_number
15
+ @manufacturer_string = manufacturer_string
16
+ @product_string = product_string
17
+ @usage = usage
18
+ @interface_number = interface_number
19
+ end
20
+
21
+ def open
22
+ HIDAPING.open vendor_id, product_id
23
+ end
24
+ end
25
+
26
+ end
data/sample/kbprog.rb ADDED
@@ -0,0 +1,190 @@
1
+ require 'hidaping'
2
+ require 'optparse'
3
+
4
+ class TeeloKB
5
+ class Error < StandardError; end
6
+ class CouldNotReadError < Error; end
7
+ class KeyboardNotFoundError < Error; end
8
+
9
+ COMMAND_VERSIONS = { 1 => {} }
10
+
11
+ %i{
12
+ version
13
+ rgblight_enable
14
+ rgblight_disable
15
+ rgblight_toggle
16
+ rgblight_mode
17
+ rgblight_sethsv
18
+ rgblight_get_mode
19
+ rgblight_get_hue
20
+ rgblight_get_sat
21
+ rgblight_get_val
22
+ }.each_with_index do |command, index|
23
+ COMMAND_VERSIONS[1][command] = index + 1
24
+ end
25
+
26
+ def self.find
27
+ HIDAPING.enumerate(0x0, 0x0).find { |dev| dev.product_string == "ErgoDox EZ" }
28
+ end
29
+
30
+ def self.open
31
+ dev = self.find
32
+
33
+ retries = 0
34
+ handle = dev.open
35
+ while !handle
36
+ retries += 1
37
+ raise KeyboardNotFoundError, "Couldn't find keyboard" if retries > 10
38
+ handle = dev.open
39
+ end
40
+
41
+ new handle
42
+ end
43
+
44
+ def initialize handle
45
+ @handle = handle
46
+ @version = protocol_version
47
+ end
48
+
49
+ def protocol_version
50
+ write [0x0, 0x1]
51
+ read(2).inject(:+)
52
+ end
53
+
54
+ def solid
55
+ rgblight_mode 0x1
56
+ end
57
+
58
+ def enable
59
+ write [0x0, _(:rgblight_enable)]
60
+ read 10
61
+ end
62
+
63
+ def disable
64
+ write [0x0, _(:rgblight_disable)]
65
+ read 10
66
+ end
67
+
68
+ def toggle
69
+ write [0x0, _(:rgblight_toggle)]
70
+ read 10
71
+ end
72
+
73
+ def mode
74
+ write [0x0, _(:rgblight_get_mode)]
75
+ read(2).last
76
+ end
77
+
78
+ def rainbow style = 0x6
79
+ rgblight_mode style
80
+ end
81
+
82
+ def rgblight_mode value
83
+ write [0x0, _(:rgblight_mode), value & 0xFF]
84
+ read 10
85
+ end
86
+ alias :mode= :rgblight_mode
87
+
88
+ def rgblight_sethsv h, s, v
89
+ write([0x0, _(:rgblight_sethsv)] + [(h >> 8) & 0xFF, h & 0xFF, s & 0xFF, v & 0xFF])
90
+ read 10
91
+ end
92
+ alias :sethsv :rgblight_sethsv
93
+
94
+ def setrgb r, g, b
95
+ sethsv *rgb2hsv(r, g, b)
96
+ end
97
+
98
+ def hue
99
+ write [0x0, _(:rgblight_get_hue)]
100
+ _, upper, lower = read 3
101
+ (upper << 8) | lower
102
+ end
103
+
104
+ def saturation
105
+ write [0x0, _(:rgblight_get_sat)]
106
+ read(2)[1]
107
+ end
108
+
109
+ def value
110
+ write [0x0, _(:rgblight_get_val)]
111
+ read(2)[1]
112
+ end
113
+
114
+ def hsv
115
+ [hue, saturation, value]
116
+ end
117
+
118
+ private
119
+
120
+ def rgb2hsv r, g, b
121
+ v = [r, g, b].max
122
+ delta = v - [r, g, b].min
123
+
124
+ s = v == 0 ? 0 : (delta / v.to_f)
125
+ h = delta == 0 ? 0 : (case v
126
+ when r then ((g - b) / delta) % 6
127
+ when g then ((b - r) / delta) + 2
128
+ when b then ((r - g) / delta) + 4
129
+ end) * 60
130
+
131
+ [h.round, (s * 255).round, v]
132
+ end
133
+
134
+ def write buf
135
+ loop do
136
+ break if @handle.write buf.pack('C*')
137
+ end
138
+ end
139
+
140
+ def read size
141
+ buf = @handle.read_timeout size, 300 # 300 ms timeout
142
+ if buf
143
+ buf.unpack('C*')
144
+ else
145
+ # Unfortunately, this seems to happen frequently.
146
+ raise CouldNotReadError, "could not read from device"
147
+ end
148
+ end
149
+
150
+ def _ command
151
+ COMMAND_VERSIONS.fetch(@version).fetch(command)
152
+ end
153
+ end
154
+
155
+ commands = []
156
+ responses = []
157
+
158
+ OptionParser.new do |opts|
159
+ opts.banner = "Usage: kbprog.rb [options]"
160
+
161
+ opts.on("--hsv=[HSV]", "Set or get HSV (comma separated)") do |v|
162
+ if v
163
+ commands << lambda { |kb| kb.sethsv(*v.split(",").map(&:to_i)) }
164
+ else
165
+ commands << lambda { |kb| responses << { hsv: kb.hsv } }
166
+ end
167
+ end
168
+ opts.on("--mode=[HSV]", "Set or get mode") do |v|
169
+ if v
170
+ commands << lambda { |kb| kb.mode = v.to_i }
171
+ else
172
+ commands << lambda { |kb| responses << { mode: kb.mode } }
173
+ end
174
+ end
175
+ end.parse!
176
+
177
+ kb = TeeloKB.open
178
+ commands.each { |cmd| cmd.call kb }
179
+ responses.each do |res|
180
+ p res
181
+ end
182
+
183
+ #p ez.mode
184
+ #ez.mode = 5
185
+ #ez.sethsv 300, 250, 255
186
+ #p ez.hsv
187
+
188
+ #rescue TeeloKB::Error => e
189
+ # exec "ruby --disable-gems -I lib:test #{__FILE__}"
190
+ #end
data/sample/set_hsv.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'hidaping'
2
+
3
+ def try_block times
4
+ times.times { x = yield; return x if x }
5
+ raise "Couldn't do it"
6
+ end
7
+
8
+ dev = HIDAPING.enumerate(0x0, 0x0).find { |dev| dev.product_string == "ErgoDox EZ" }
9
+
10
+ handle = try_block(10) { dev.open }
11
+ p handle
12
+
13
+ h = 0 # 0x0 to 0x168 (which is 360 base 10)
14
+ s = 0xFF # 0x0 to 0xFF
15
+ v = 0xFF # 0x0 to 0xFF
16
+
17
+ try_block(10) do
18
+ handle.write [0x0, 0x6, (h >> 8) & 0xFF, h & 0xFF, s, v].pack('C*')
19
+ end
20
+
21
+ handle.read_timeout 10, 300 # 300 ms timeout
data/sig/myhidapi.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module HIDAPING
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
data/test/joycon.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'hidaping'
2
+
3
+ devices = HIDAPING.enumerate(0x0, 0x0)
4
+
5
+ for d in devices
6
+ printf("0x%04x, 0x%04x, %s\n", d.vendor_id, d.product_id, d.product_string)
7
+ end
8
+
9
+ handle = HIDAPING.open(0x057e, 0x2007)
10
+ handle.send_feature_report("\x01\x48\x00\x01\x40\x40\x00\x01\x40\x40\x00")
11
+
12
+ while true
13
+ report_raw = handle.read_timeout(64, 500)
14
+ data = report_raw.unpack("a17va*")
15
+ p data[1]
16
+ end
data/test/test.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'hidaping'
2
+
3
+ devices = HIDAPING.enumerate(0x0, 0x0)
4
+
5
+ for d in devices
6
+ printf("0x%04x, 0x%04x, %s\n", d.vendor_id, d.product_id, d.product_string)
7
+ end
8
+
9
+ if devices.length > 0
10
+ handle = devices[0].open()
11
+
12
+ report_raw = handle.read_timeout(64, 500)
13
+ p report_raw
14
+ end
@@ -0,0 +1,33 @@
1
+ require 'minitest/autorun'
2
+ require 'hidaping'
3
+
4
+ class TestHIDAPING < MiniTest::Test
5
+ def test_it_works
6
+ devices = HIDAPING.enumerate 0x0, 0x0
7
+ dev = devices.find { |dev| dev.product_string == "ErgoDox EZ" }
8
+
9
+ handle = dev.open
10
+
11
+ while !handle
12
+ p "retry"
13
+ handle = dev.open
14
+ end
15
+
16
+ p handle
17
+
18
+ buf = [0x0, 0x3, 35]
19
+ loop do
20
+ break if handle.write buf.pack('C*')
21
+ end
22
+ puts "done writing"
23
+
24
+ buf = handle.read_timeout 1, 500
25
+ p buf
26
+ end
27
+
28
+ def test_can_enumerate_by_ids
29
+ devices = HIDAPING.enumerate(0xfeed, 0x1307)
30
+
31
+ refute_predicate devices, :empty?
32
+ end
33
+ end
data/test/xbox360.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'hidaping'
2
+
3
+ devices = HIDAPING.enumerate(0x0, 0x0)
4
+
5
+ for d in devices
6
+ printf("0x%04x, 0x%04x, %s\n", d.vendor_id, d.product_id, d.product_string)
7
+ end
8
+
9
+ handle = HIDAPING.open(0x045e, 0x028e)
10
+
11
+ while true
12
+ report_raw = handle.read_timeout(64, 500)
13
+ data = report_raw.unpack("vvvvvSb*")
14
+ p data
15
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hidaping
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Repy
8
+ - Aaron Patterson
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |
14
+ This is a small wrapper around HIDAPI.
15
+ I couldn't get other HIDAPI wrappers to work, so I wrote this one.
16
+ I'm using it to communicate with my keyboard, so it really only supports enough of the HIDAPI to do that.
17
+
18
+ This gem is a fork from the original work by Aaron Patterson (@tenderlove).
19
+ email:
20
+ - gamecube02+git@gmail.com
21
+ - tenderlove@ruby-lang.org
22
+ executables: []
23
+ extensions:
24
+ - ext/hidaping/extconf.rb
25
+ extra_rdoc_files: []
26
+ files:
27
+ - ".clang-format"
28
+ - ".github/workflows/gem-push.yml"
29
+ - ".github/workflows/test.yml"
30
+ - ".ruby-version"
31
+ - CHANGELOG.md
32
+ - LICENSE.txt
33
+ - README.md
34
+ - Rakefile
35
+ - ext/hidaping/common.h
36
+ - ext/hidaping/extconf.rb
37
+ - ext/hidaping/hidaping.c
38
+ - ext/hidaping/hidaping.h
39
+ - ext/hidaping/hidaping_device.c
40
+ - ext/hidaping/hidaping_device.h
41
+ - ext/hidaping/hidaping_util.h
42
+ - lib/hidaping.rb
43
+ - lib/hidaping/version.rb
44
+ - sample/kbprog.rb
45
+ - sample/set_hsv.rb
46
+ - sig/myhidapi.rbs
47
+ - test/joycon.rb
48
+ - test/test.rb
49
+ - test/test_myhidapi.rb
50
+ - test/xbox360.rb
51
+ homepage: https://github.com/Repy/hidaping
52
+ licenses:
53
+ - MIT
54
+ metadata:
55
+ homepage_uri: https://github.com/Repy/hidaping
56
+ source_code_uri: https://github.com/Repy/hidaping
57
+ rdoc_options: []
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 3.2.0
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubygems_version: 3.7.1
72
+ specification_version: 4
73
+ summary: A wrapper around hidapi for Ruby
74
+ test_files: []