diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 78461f4..7cc9c11 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -10,7 +10,7 @@ jobs: fail-fast: false matrix: platform: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.8", "3.12"] + python-version: ["3.8", "3.13"] system-hidapi: ["", "--with-system-hidapi"] libusb-backend: ["", "--with-libusb"] exclude: diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index d79e1cf..af476d5 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -17,25 +17,23 @@ jobs: submodules: true - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: "3.13" - name: Setup QEMU if: ${{ runner.os == 'Linux' }} uses: docker/setup-qemu-action@v3 - name: Build wheels - uses: pypa/cibuildwheel@v2.16.2 + uses: pypa/cibuildwheel@v2.21.3 env: CIBW_BUILD: "cp3*-*" - CIBW_SKIP: "*-musllinux_*" CIBW_ARCHS_WINDOWS: "auto" CIBW_ARCHS_MACOS: "x86_64 arm64" CIBW_ARCHS_LINUX: "x86_64 i686 aarch64" CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 - CIBW_BEFORE_ALL_LINUX: yum install -y libusb1-devel libudev-devel - HIDAPI_WITH_LIBUSB: 1 + CIBW_BEFORE_ALL_LINUX: yum install -y libusb1-devel libudev-devel || apk add --upgrade libusb-dev eudev-dev - name: Store artifacts uses: actions/upload-artifact@v3 diff --git a/chid.pxd b/chid.pxd index 798c404..523a462 100644 --- a/chid.pxd +++ b/chid.pxd @@ -37,6 +37,7 @@ cdef extern from "": int hid_read(hid_device* device, unsigned char* data, int max_length) nogil int hid_read_timeout(hid_device* device, unsigned char* data, int max_length, int milliseconds) nogil int hid_set_nonblocking(hid_device* device, int value) + int hid_get_report_descriptor(hid_device* device, unsigned char *data, int length) nogil int hid_send_feature_report(hid_device* device, unsigned char *data, int length) nogil int hid_get_feature_report(hid_device* device, unsigned char *data, int length) nogil int hid_get_input_report(hid_device* device, unsigned char *data, int length) nogil diff --git a/docs/conf.py b/docs/conf.py index 4ad48b4..030c354 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -30,7 +30,6 @@ # ones. extensions = [ "sphinx.ext.autodoc", - "recommonmark", ] # Add any paths that contain templates here, relative to this directory. @@ -38,7 +37,6 @@ source_suffix = { ".rst": "restructuredtext", - ".md": "markdown", } # List of patterns, relative to source directory, that match files and diff --git a/docs/examples.md b/docs/examples.md deleted file mode 100644 index c01d434..0000000 --- a/docs/examples.md +++ /dev/null @@ -1,61 +0,0 @@ -# Examples - -## Finding devices - -List all info of all devices with: - -```python -import hid - -for device_dict in hid.enumerate(): - keys = list(device_dict.keys()) - keys.sort() - for key in keys: - print("%s : %s" % (key, device_dict[key])) - print() - -``` - -## Connecting, reading and writing - -```python -try: - print("Opening the device") - - h = hid.device() - h.open(0x534C, 0x0001) # TREZOR VendorID/ProductID - - print("Manufacturer: %s" % h.get_manufacturer_string()) - print("Product: %s" % h.get_product_string()) - print("Serial No: %s" % h.get_serial_number_string()) - - # enable non-blocking mode - h.set_nonblocking(1) - - # write some data to the device - print("Write the data") - h.write([0, 63, 35, 35] + [0] * 61) - - # wait - time.sleep(0.05) - - # read back the answer - print("Read the data") - while True: - d = h.read(64) - if d: - print(d) - else: - break - - print("Closing the device") - h.close() - -except IOError as ex: - print(ex) - print("You probably don't have the hard-coded device.") - print("Update the h.open() line in this script with the one") - print("from the enumeration list output above and try again.") - -print("Done") -``` diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..7558890 --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,64 @@ +Examples +======== + +Finding devices +--------------- + +List all info of all devices with: + +.. code-block:: python + + import hid + + for device_dict in hid.enumerate(): + keys = list(device_dict.keys()) + keys.sort() + for key in keys: + print("%s : %s" % (key, device_dict[key])) + print() + + +Connecting, reading and writing +------------------------------- + +.. code-block:: python + + try: + print("Opening the device") + + h = hid.device() + h.open(0x534C, 0x0001) # TREZOR VendorID/ProductID + + print("Manufacturer: %s" % h.get_manufacturer_string()) + print("Product: %s" % h.get_product_string()) + print("Serial No: %s" % h.get_serial_number_string()) + + # enable non-blocking mode + h.set_nonblocking(1) + + # write some data to the device + print("Write the data") + h.write([0, 63, 35, 35] + [0] * 61) + + # wait + time.sleep(0.05) + + # read back the answer + print("Read the data") + while True: + d = h.read(64) + if d: + print(d) + else: + break + + print("Closing the device") + h.close() + + except IOError as ex: + print(ex) + print("You probably don't have the hard-coded device.") + print("Update the h.open() line in this script with the one") + print("from the enumeration list output above and try again.") + + print("Done") diff --git a/docs/index.rst b/docs/index.rst index e8c2e03..ad94090 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -11,7 +11,7 @@ Welcome to HIDAPI's documentation! :caption: Contents: Home - examples.md + examples.rst api.rst diff --git a/hid.pyx b/hid.pyx index 674aec7..c90fceb 100644 --- a/hid.pyx +++ b/hid.pyx @@ -5,7 +5,7 @@ from chid cimport * from libc.stddef cimport wchar_t, size_t -__version__ = "0.14.0" +__version__ = "0.14.0.post3" hid_init() @@ -330,6 +330,38 @@ cdef class device: result = hid_send_feature_report(c_hid, cbuff, c_buff_len) return result + def get_report_descriptor(self, int max_length=4096): + """Return the report descriptor up to max_length bytes. + If max_length is bigger than the actual descriptor, the full descriptor will be returned. + + :param max_length: Maximum number of bytes to read, must be positive + :type max_length: int + + :return: + :rtype: List[int] + :raises ValueError: If connection is not opened. + :raises IOError: If read error + """ + if self._c_hid == NULL: + raise ValueError('not open') + + cdef unsigned char* cbuff + cdef size_t c_descriptor_length = max(1, max_length) + cdef hid_device * c_hid = self._c_hid + cdef int n + result = [] + try: + cbuff = malloc(max_length) + with nogil: + n = hid_get_report_descriptor(c_hid, cbuff, c_descriptor_length) + if n < 0: + raise IOError('read error') + for i in range(n): + result.append(cbuff[i]) + finally: + free(cbuff) + return result + def get_feature_report(self, int report_num, int max_length): """Receive feature report.