From a35abd0e06a42d531585647a503062e940d406b4 Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Thu, 20 Mar 2025 20:57:52 +0100 Subject: [PATCH 1/8] termux-api: add patches to build termux-usb library as well --- .../0001-Add-termux-usb-library.patch | 558 ++++++++++++++++++ packages/termux-api/build.sh | 7 +- 2 files changed, 564 insertions(+), 1 deletion(-) create mode 100644 packages/termux-api/0001-Add-termux-usb-library.patch diff --git a/packages/termux-api/0001-Add-termux-usb-library.patch b/packages/termux-api/0001-Add-termux-usb-library.patch new file mode 100644 index 000000000000000..709276b2d00b1d4 --- /dev/null +++ b/packages/termux-api/0001-Add-termux-usb-library.patch @@ -0,0 +1,558 @@ +From 69db489ee69908afd7d52fe0f48b2f518cd43148 Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Sun, 16 Mar 2025 09:53:35 +0100 +Subject: [PATCH] Add termux-usb library + +It contains the following functions: + +* termux_usb_get_device_list +* termux_usb_free_device_list +* termux_usb_open +* termux_usb_get_config_descriptor +* termux_usb_free_config_descriptor + +These functions are replacements for the corresponding libusb_* +functions, kind of. The termux variants uses structs like struct +termux_usb_device and struct termux_usb_config_descriptor that are +similar, but not identical, to libusb_device and +libusb_config_descriptor. They work by calling termux-api and get back +information about devices and configs as a protobuf stream. + +The free functions also frees all array elements, whereas the libusb +variants only frees elements that have a reference count of 1 or +less. For the termux variants where do not keep track of the reference +count, so information that should be kept around should be memcpy'ed +or similar before termux_usb_free_* is called. + +termux_usb_open requires user interaction. It calls termux-api which +opens the device through the android API, which shows a prompt to the +user to accept or deny opening the device. +--- + CMakeLists.txt | 2 + + usb/CMakeLists.txt | 33 ++++++ + usb/proto/UsbAPI.proto | 57 +++++++++ + usb/termux-usb.c | 259 +++++++++++++++++++++++++++++++++++++++++ + usb/termux-usb.h | 128 ++++++++++++++++++++ + 5 files changed, 479 insertions(+) + create mode 100644 usb/CMakeLists.txt + create mode 100644 usb/proto/UsbAPI.proto + create mode 100644 usb/termux-usb.c + create mode 100644 usb/termux-usb.h + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c8b1f9e5995d..00f18a567e64 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -11,6 +11,8 @@ set_target_properties(termux-api-static PROPERTIES OUTPUT_NAME termux-api) + add_executable(termux-api-broadcast termux-api-broadcast.c) + target_link_libraries(termux-api-broadcast termux-api-static) + ++add_subdirectory(usb) ++ + # TODO: get list through regex or similar + set(script_files + scripts/termux-api-start +diff --git a/usb/CMakeLists.txt b/usb/CMakeLists.txt +new file mode 100644 +index 000000000000..d9d61419e7ea +--- /dev/null ++++ b/usb/CMakeLists.txt +@@ -0,0 +1,33 @@ ++cmake_minimum_required(VERSION 3.10.0) ++project(termux-usb) ++ ++find_package(PkgConfig REQUIRED) ++pkg_check_modules(libusb REQUIRED libusb-1.0) ++# For test building on Linux below needs to be changed to ++# find_package(Protobuf REQUIRED CONFIG) for some reason.. ++find_package(Protobuf REQUIRED) ++protobuf_generate( ++ LANGUAGE C ++ GENERATE_EXTENSIONS .pb-c.c ++ OUT_VAR PROTO_SRCS ++ PROTOS proto/UsbAPI.proto ++) ++add_library(termux-usb SHARED termux-usb.c termux-usb.h ${PROTO_SRCS} ${PROTO_HDRS}) ++ ++target_include_directories( ++ termux-usb ++ PUBLIC ${Protobuf_INCLUDE_DIRS} ${libusb_INCLUDE_DIRS} ++ PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/proto ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR} ++) ++target_link_libraries(termux-usb PUBLIC ${Protobuf_LIBRARIES} ${CMAKE_BINARY_DIR}/libtermux-api.so -lprotobuf-c ${libusb_LIBRARIES}) ++ ++install( ++ FILES ++ ${CMAKE_CURRENT_BINARY_DIR}/libtermux-usb.so ++ TYPE LIB ++) ++ ++install( ++ FILES ${CMAKE_CURRENT_SOURCE_DIR}/termux-usb.h ++ TYPE INCLUDE ++) +diff --git a/usb/proto/UsbAPI.proto b/usb/proto/UsbAPI.proto +new file mode 100644 +index 000000000000..5a1d89c5cb6a +--- /dev/null ++++ b/usb/proto/UsbAPI.proto +@@ -0,0 +1,57 @@ ++syntax = "proto3"; ++ ++package usbapi; ++ ++option java_package = "com.termux.api"; ++option java_outer_classname = "UsbAPIProto"; ++ ++/* Modelled after libusb's struct libusb_endpoint_descriptor */ ++message termuxUsbEndpointDescriptor { ++ int32 endpointAddress = 1; ++ int32 attributes = 2; ++ int32 maxPacketSize = 3; ++ int32 interval = 4; ++} ++ ++/* Modelled after libusb's struct libusb_interface_descriptor */ ++message termuxUsbInterfaceDescriptor { ++ int32 alternateSetting = 1; ++ int32 interfaceClass = 2; ++ int32 interfaceSubclass = 3; ++ int32 interfaceProtocol = 4; ++ string interface = 5; ++ repeated termuxUsbEndpointDescriptor endpoint = 6; ++} ++ ++/* Modelled after libusb's struct libusb_config_descriptor */ ++message termuxUsbConfigDescriptor { ++ int32 configurationValue = 1; ++ int32 maxPower = 2; ++ string configuration = 3; ++ repeated termuxUsbInterfaceDescriptor interface = 4; ++} ++ ++/* Modelled after libusb's struct libusb_device_descriptor */ ++message termuxUsbDeviceDescriptor { ++ int32 configurationCount = 1; ++ int32 deviceClass = 2; ++ int32 deviceProtocol = 3; ++ int32 deviceSubclass = 4; ++ int32 productId = 5; ++ int32 vendorId = 6; ++ string manufacturerName = 7; ++ string productName = 8; ++ string serialNumber = 9; ++} ++ ++/* Loosely modelled after libusb's internal struct libusb_device */ ++message termuxUsbDevice { ++ int32 busNumber = 1; ++ int32 portNumber = 2; ++ string deviceAddress = 3; ++ termuxUsbDeviceDescriptor device = 4; ++} ++ ++message termuxUsb { ++ repeated termuxUsbDevice device = 1; ++} +diff --git a/usb/termux-usb.c b/usb/termux-usb.c +new file mode 100644 +index 000000000000..4c55e2ae5032 +--- /dev/null ++++ b/usb/termux-usb.c +@@ -0,0 +1,259 @@ ++#include ++#include ++#include ++#include ++ ++#include "termux-api.h" ++#include "termux-usb.h" ++#include "UsbAPI.pb-c.h" ++ ++#define BUF_SIZE 1024 ++#define TERMUX_PREFIX "/data/data/com.termux/files/usr" ++ ++static size_t read_buffer (unsigned int buf_size, uint8_t *out, FILE *fp) ++{ ++ size_t cur_len = 0; ++ size_t nread; ++ while ((nread = fread(out + cur_len, 1, buf_size - cur_len, fp)) != 0) ++ { ++ cur_len += nread; ++ if (cur_len >= buf_size) ++ { ++ fprintf(stderr, "max message length exceeded\n"); ++ exit(EXIT_FAILURE); ++ } ++ } ++ return cur_len; ++} ++ ++ssize_t termux_usb_get_device_list(struct termux_usb_device ***list) ++{ ++ Usbapi__TermuxUsb *container; ++ struct termux_usb_device **ret; ++ uint8_t buf[BUF_SIZE]; ++ FILE *fp; ++ unsigned int num_devices; ++ ++ fp = popen(TERMUX_PREFIX "/libexec/termux-api Usb -a getDevices", "r"); ++ if (fp == NULL) { ++ perror("popen"); ++ exit(EXIT_FAILURE); ++ } ++ ++ size_t msg_len = read_buffer(BUF_SIZE, buf, fp); ++ if (pclose(fp)) { ++ fprintf(stderr, "Error closing protobuf stream\n"); ++ return -1; ++ } ++ ++ container = usbapi__termux_usb__unpack(NULL, msg_len, buf); ++ if (container == NULL) ++ { ++ fprintf(stderr, "Error unpacking incoming message\n"); ++ return -1; ++ } ++ ++ ret = (struct termux_usb_device **) calloc(container->n_device + 1, sizeof(struct termux_usb_device *)); ++ if (!ret) { ++ fprintf(stderr, "Failed to allocate memory\n"); ++ usbapi__termux_usb__free_unpacked(container, NULL); ++ return -1; ++ } ++ ++ ret[container->n_device] = NULL; ++ for (size_t i = 0; i < container->n_device; i++) { ++ Usbapi__TermuxUsbDevice *msg = container->device[i]; ++ Usbapi__TermuxUsbDeviceDescriptor *desc = msg->device; ++ struct termux_usb_device *dev = (struct termux_usb_device *) malloc(sizeof(struct termux_usb_device)); ++ struct termux_usb_device_descriptor *dev_desc = (struct termux_usb_device_descriptor *) malloc(sizeof(struct termux_usb_device_descriptor)); ++ ++ dev->bus_number = msg->busnumber; ++ dev->port_number = msg->portnumber; ++ ++ dev->device_address = (char *) malloc(strlen(msg->deviceaddress)+1); ++ strcpy(dev->device_address, msg->deviceaddress); ++ ++ dev_desc->bDeviceClass = desc->deviceclass; ++ dev_desc->bDeviceSubClass = desc->devicesubclass; ++ dev_desc->bDeviceProtocol = desc->deviceprotocol; ++ dev_desc->idVendor = desc->vendorid; ++ dev_desc->idProduct = desc->productid; ++ ++ dev_desc->manufacturer = (char *) malloc(strlen(desc->manufacturername)+1); ++ strcpy(dev_desc->manufacturer, desc->manufacturername); ++ ++ dev_desc->product = (char *) malloc(strlen(desc->productname)+1); ++ strcpy(dev_desc->product, desc->productname); ++ ++ dev_desc->serialNumber = (char *) malloc(strlen(desc->serialnumber)+1); ++ strcpy(dev_desc->serialNumber, desc->serialnumber); ++ ++ dev_desc->bNumConfigurations = desc->configurationcount; ++ ++ dev->device_descriptor = dev_desc; ++ ret[i] = dev; ++ } ++ *list = ret; ++ ++ num_devices = container->n_device; ++ usbapi__termux_usb__free_unpacked(container, NULL); ++ return num_devices; ++} ++ ++void termux_usb_free_device_list(struct termux_usb_device **list) ++{ ++ int i = 0; ++ struct termux_usb_device *dev; ++ while ((dev = list[i++]) != NULL) { ++ free(dev->device_descriptor->manufacturer); ++ free(dev->device_descriptor->product); ++ free(dev->device_descriptor->serialNumber); ++ free(dev->device_descriptor); ++ free(dev->device_address); ++ free(dev); ++ } ++ free(list); ++} ++ ++intptr_t termux_usb_open_address(char *address) ++{ ++ int argc = 10; ++ char *argv[argc]; ++ argv[0] = "termux-usb"; ++ argv[1] = "Usb"; ++ argv[2] = "-a"; ++ argv[3] = "open"; ++ argv[4] = "--ez"; ++ argv[5] = "request"; ++ argv[6] = "true"; ++ argv[7] = "--es"; ++ argv[8] = "device"; ++ argv[9] = address; ++ ++ int fd = run_api_command(argc, argv); ++ ++ return (intptr_t) fd; ++} ++ ++intptr_t termux_usb_open(struct termux_usb_device *dev) ++{ ++ return termux_usb_open_address(dev->device_address); ++} ++ ++int termux_usb_get_config_descriptor(struct termux_usb_device *dev, int config_index, ++ struct termux_usb_config_descriptor **conf_desc) ++{ ++ Usbapi__TermuxUsbConfigDescriptor *confDesc; ++ uint8_t buf[BUF_SIZE]; ++ FILE *fp; ++ ++ char cmd[256]; ++ char *base_cmd = TERMUX_PREFIX "/libexec/termux-api Usb -a getConfigDescriptor --es device %s --ei config %d"; ++ snprintf(cmd, sizeof(cmd)-1, base_cmd, dev->device_address, config_index); ++ fp = popen(cmd, "r"); ++ if (fp == NULL) { ++ perror("popen"); ++ return 1; ++ } ++ ++ size_t msg_len = read_buffer(BUF_SIZE, buf, fp); ++ if (pclose(fp)) { ++ fprintf(stderr, "Error closing protobuf stream\n"); ++ return -1; ++ } ++ ++ confDesc = usbapi__termux_usb_config_descriptor__unpack(NULL, msg_len, buf); ++ if (confDesc == NULL) { ++ fprintf(stderr, "Error unpacking incoming config descriptor message\n"); ++ return -1; ++ } ++ ++ struct termux_usb_config_descriptor *conf; ++ conf = (struct termux_usb_config_descriptor *) malloc(sizeof(struct termux_usb_config_descriptor)); ++ if (conf == NULL) { ++ fprintf(stderr, "Memory allocation failed\n"); ++ usbapi__termux_usb_config_descriptor__free_unpacked(confDesc, NULL); ++ return -1; ++ } ++ ++ conf->bConfigurationValue = confDesc->configurationvalue; ++ conf->configuration = (char *) malloc(strlen(confDesc->configuration)+1); ++ if (conf->configuration == NULL) { ++ fprintf(stderr, "Memory allocation failed\n"); ++ free(conf); ++ usbapi__termux_usb_config_descriptor__free_unpacked(confDesc, NULL); ++ return -1; ++ } ++ strcpy(conf->configuration, confDesc->configuration); ++ conf->MaxPower = confDesc->maxpower; ++ conf->bNumInterfaces = confDesc->n_interface; ++ ++ conf->interface = (struct termux_usb_interface_descriptor *) calloc(confDesc->n_interface, sizeof(struct termux_usb_interface_descriptor)); ++ if (conf->interface == NULL) { ++ fprintf(stderr, "Memory allocation failed\n"); ++ free(conf->configuration); ++ free(conf); ++ usbapi__termux_usb_config_descriptor__free_unpacked(confDesc, NULL); ++ return -1; ++ } ++ ++ for (size_t i = 0; i < confDesc->n_interface; i++) { ++ Usbapi__TermuxUsbInterfaceDescriptor *intfDesc = confDesc->interface[i]; ++ ++ conf->interface[i].bAlternateSetting = intfDesc->alternatesetting; ++ conf->interface[i].bNumEndpoints = intfDesc->n_endpoint; ++ conf->interface[i].bInterfaceClass = intfDesc->interfaceclass; ++ conf->interface[i].bInterfaceSubClass = intfDesc->interfacesubclass; ++ conf->interface[i].bInterfaceProtocol = intfDesc->interfaceprotocol; ++ conf->interface[i].interface = (char *) malloc(strlen(intfDesc->interface)+1); ++ if (conf->interface[i].interface == NULL) { ++ fprintf(stderr, "Memory allocation failed\n"); ++ for (size_t j = 0; j < i; i++) { ++ free(conf->interface[i].endpoint); ++ free(conf->interface[i].interface); ++ } ++ free(conf->interface); ++ free(conf->configuration); ++ free(conf); ++ usbapi__termux_usb_config_descriptor__free_unpacked(confDesc, NULL); ++ return -1; ++ } ++ strcpy(conf->interface[i].interface, intfDesc->interface); ++ ++ conf->interface[i].endpoint = (struct termux_usb_endpoint_descriptor *) calloc(intfDesc->n_endpoint, sizeof(struct termux_usb_endpoint_descriptor)); ++ if (conf->interface[i].endpoint == NULL) { ++ fprintf(stderr, "Memory allocation failed\n"); ++ for (size_t j = 0; j < i; i++) { ++ free(conf->interface[i].endpoint); ++ free(conf->interface[i].interface); ++ } ++ free(conf->interface); ++ free(conf->configuration); ++ free(conf); ++ usbapi__termux_usb_config_descriptor__free_unpacked(confDesc, NULL); ++ } ++ ++ for (size_t j = 0; j < intfDesc->n_endpoint; j++) { ++ Usbapi__TermuxUsbEndpointDescriptor *endpointDesc = intfDesc->endpoint[j]; ++ conf->interface[i].endpoint[j].bEndpointAddress = endpointDesc->endpointaddress; ++ conf->interface[i].endpoint[j].bmAttributes = endpointDesc->attributes; ++ conf->interface[i].endpoint[j].wMaxPacketSize = endpointDesc->maxpacketsize; ++ conf->interface[i].endpoint[j].bInterval = endpointDesc->interval; ++ } ++ } ++ *conf_desc = conf; ++ ++ usbapi__termux_usb_config_descriptor__free_unpacked(confDesc, NULL); ++ return 0; ++} ++ ++void termux_usb_free_config_descriptor(struct termux_usb_config_descriptor *conf_desc) ++{ ++ for (int i = 0; i < conf_desc->bNumInterfaces; i++) { ++ free(conf_desc->interface[i].endpoint); ++ free(conf_desc->interface[i].interface); ++ } ++ free(conf_desc->interface); ++ free(conf_desc->configuration); ++ free(conf_desc); ++} +diff --git a/usb/termux-usb.h b/usb/termux-usb.h +new file mode 100644 +index 000000000000..a59821dd4bcf +--- /dev/null ++++ b/usb/termux-usb.h +@@ -0,0 +1,128 @@ ++#include ++ ++#if defined(__cplusplus) ++extern "C" { ++#endif ++ ++struct termux_usb_device_descriptor { ++ /** USB-IF class code for the device. See \ref libusb_class_code. */ ++ uint8_t bDeviceClass; ++ ++ /** USB-IF subclass code for the device, qualified by the bDeviceClass ++ * value */ ++ uint8_t bDeviceSubClass; ++ ++ /** USB-IF protocol code for the device, qualified by the bDeviceClass and ++ * bDeviceSubClass values */ ++ uint8_t bDeviceProtocol; ++ ++ /** USB-IF vendor ID */ ++ uint16_t idVendor; ++ ++ /** USB-IF product ID */ ++ uint16_t idProduct; ++ ++ /** String describing manufacturer */ ++ char *manufacturer; ++ ++ /** String describing product */ ++ char *product; ++ ++ /** String containing device serial number */ ++ char *serialNumber; ++ ++ /** Number of possible configurations */ ++ uint8_t bNumConfigurations; ++}; ++ ++ ++struct termux_usb_device { ++ uint8_t bus_number; ++ uint8_t port_number; ++ char *device_address; ++ ++ struct termux_usb_device_descriptor *device_descriptor; ++}; ++ ++struct termux_usb_endpoint_descriptor { ++ /** The address of the endpoint described by this descriptor. Bits 0:3 are ++ * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, ++ * see \ref libusb_endpoint_direction. */ ++ uint8_t bEndpointAddress; ++ ++ /** Attributes which apply to the endpoint when it is configured using ++ * the bConfigurationValue. Bits 0:1 determine the transfer type and ++ * correspond to \ref libusb_endpoint_transfer_type. Bits 2:3 are only used ++ * for isochronous endpoints and correspond to \ref libusb_iso_sync_type. ++ * Bits 4:5 are also only used for isochronous endpoints and correspond to ++ * \ref libusb_iso_usage_type. Bits 6:7 are reserved. */ ++ uint8_t bmAttributes; ++ ++ /** Maximum packet size this endpoint is capable of sending/receiving. */ ++ uint16_t wMaxPacketSize; ++ ++ /** Interval for polling endpoint for data transfers. */ ++ uint8_t bInterval; ++}; ++ ++struct termux_usb_interface_descriptor { ++ /** Number of this interface */ ++ uint8_t bInterfaceNumber; ++ ++ /** Value used to select this alternate setting for this interface */ ++ uint8_t bAlternateSetting; ++ ++ /** Number of endpoints used by this interface (excluding the control ++ * endpoint). */ ++ uint8_t bNumEndpoints; ++ ++ /** USB-IF class code for this interface. See \ref libusb_class_code. */ ++ uint8_t bInterfaceClass; ++ ++ /** USB-IF subclass code for this interface, qualified by the ++ * bInterfaceClass value */ ++ uint8_t bInterfaceSubClass; ++ ++ /** USB-IF protocol code for this interface, qualified by the ++ * bInterfaceClass and bInterfaceSubClass values */ ++ uint8_t bInterfaceProtocol; ++ ++ /** String describing this interface */ ++ char *interface; ++ ++ /** Array of endpoint descriptors. This length of this array is determined ++ * by the bNumEndpoints field. */ ++ struct termux_usb_endpoint_descriptor *endpoint; ++}; ++ ++struct termux_usb_config_descriptor { ++ /** Number of interfaces supported by this configuration */ ++ uint8_t bNumInterfaces; ++ ++ /** Identifier value for this configuration */ ++ uint8_t bConfigurationValue; ++ ++ /** String describing this configuration */ ++ char *configuration; ++ ++ /** Maximum power consumption of the USB device from this bus in this ++ * configuration when the device is fully operation. Expressed in units ++ * of 2 mA when the device is operating in high-speed mode and in units ++ * of 8 mA when the device is operating in super-speed mode. */ ++ uint8_t MaxPower; ++ ++ /** Array of interfaces supported by this configuration. The length of ++ * this array is determined by the bNumInterfaces field. */ ++ struct termux_usb_interface_descriptor *interface; ++}; ++ ++ssize_t termux_usb_get_device_list(struct termux_usb_device ***); ++void termux_usb_free_device_list(struct termux_usb_device **); ++intptr_t termux_usb_open_address(char *); ++intptr_t termux_usb_open(struct termux_usb_device *); ++int termux_usb_get_config_descriptor(struct termux_usb_device *, int, ++ struct termux_usb_config_descriptor **); ++void termux_usb_free_config_descriptor(struct termux_usb_config_descriptor *); ++#if defined(__cplusplus) ++} ++#endif +-- +2.49.0 + diff --git a/packages/termux-api/build.sh b/packages/termux-api/build.sh index 8ca300f357f1294..d9d419dcf339cad 100644 --- a/packages/termux-api/build.sh +++ b/packages/termux-api/build.sh @@ -3,7 +3,12 @@ TERMUX_PKG_DESCRIPTION="Termux API commands (install also the Termux:API app)" TERMUX_PKG_LICENSE="MIT" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION=0.59.1 +TERMUX_PKG_REVISION=1 TERMUX_PKG_SRCURL=https://github.com/termux/termux-api-package/archive/v${TERMUX_PKG_VERSION}.tar.gz TERMUX_PKG_SHA256=92c2da07991a0191735539428aabb793e0ddb8e33baac305bcf38d77aa1eda80 TERMUX_PKG_BUILD_IN_SRC=true -TERMUX_PKG_DEPENDS="bash, util-linux, termux-am (>= 0.8.0)" +TERMUX_PKG_DEPENDS="bash, libusb, libprotobuf-c, util-linux, termux-am (>= 0.8.0)" + +termux_step_pre_configure() { + termux_setup_protobuf +} From a1e04a347365e3ff18d1957f255533e79d5584f3 Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Sat, 8 Mar 2025 02:58:13 +0100 Subject: [PATCH 2/8] enhance(main/hidapi): use termux-usb for enumerating devices On Android we cannot use libusb_get_device_list and libusb_open directly. Instead we need to enumerate and open devices through the Android API. In Termux we have termux-api for handling this communication, so we can replace the libusb_.. functions with termux_usb_.. variants. WIP for now, code segfaults when it tries to open a device. --- ...ld-with-iconv-when-__TERMUX__-is-set.patch | 68 ++++ ...ome-functions-with-termux_usb-varian.patch | 322 ++++++++++++++++++ ...ath-to-device_address-from-termux-us.patch | 48 +++ packages/hidapi/build.sh | 7 +- .../hidapi/libusb-iconv-CMakeLists.txt.patch | 20 -- packages/hidapi/termux-iconv-hid.c.patch | 29 -- 6 files changed, 444 insertions(+), 50 deletions(-) create mode 100644 packages/hidapi/0001-libusb-build-with-iconv-when-__TERMUX__-is-set.patch create mode 100644 packages/hidapi/0002-libusb-replace-some-functions-with-termux_usb-varian.patch create mode 100644 packages/hidapi/0003-libusb-hid-set-path-to-device_address-from-termux-us.patch delete mode 100644 packages/hidapi/libusb-iconv-CMakeLists.txt.patch delete mode 100644 packages/hidapi/termux-iconv-hid.c.patch diff --git a/packages/hidapi/0001-libusb-build-with-iconv-when-__TERMUX__-is-set.patch b/packages/hidapi/0001-libusb-build-with-iconv-when-__TERMUX__-is-set.patch new file mode 100644 index 000000000000000..21712877baaaaea --- /dev/null +++ b/packages/hidapi/0001-libusb-build-with-iconv-when-__TERMUX__-is-set.patch @@ -0,0 +1,68 @@ +From aa18490ba5ce05a43b7046006ecc258633255c54 Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Tue, 4 Mar 2025 11:00:35 +0100 +Subject: [PATCH] libusb: build with iconv when __TERMUX__ is set + +Android does not have libiconv, but termux does, so add some +__TERMUX__ checks. +--- + libusb/CMakeLists.txt | 4 ---- + libusb/hid.c | 6 +++--- + 2 files changed, 3 insertions(+), 7 deletions(-) + +diff --git a/libusb/CMakeLists.txt b/libusb/CMakeLists.txt +index 6cd48c4f70f6..beba0a3fdb33 100644 +--- a/libusb/CMakeLists.txt ++++ b/libusb/CMakeLists.txt +@@ -22,7 +22,6 @@ target_link_libraries(hidapi_libusb PRIVATE Threads::Threads) + if(HIDAPI_NO_ICONV) + target_compile_definitions(hidapi_libusb PRIVATE NO_ICONV) + else() +- if(NOT ANDROID) + include(CheckCSourceCompiles) + + if(NOT CMAKE_VERSION VERSION_LESS 3.11) +@@ -77,9 +76,6 @@ else() + if(HIDAPI_ICONV_CONST) + target_compile_definitions(hidapi_libusb PRIVATE "ICONV_CONST=const") + endif() +- else() +- # On Android Iconv is disabled on the code level anyway, so no issue; +- endif() + endif() + + set_target_properties(hidapi_libusb +diff --git a/libusb/hid.c b/libusb/hid.c +index 188e536d537d..d3861f772673 100644 +--- a/libusb/hid.c ++++ b/libusb/hid.c +@@ -42,7 +42,7 @@ + + /* GNU / LibUSB */ + #include +-#if !defined(__ANDROID__) && !defined(NO_ICONV) ++#if (!defined(__ANDROID__) || defined(__TERMUX__)) && !defined(NO_ICONV) + #include + #ifndef ICONV_CONST + #define ICONV_CONST +@@ -406,7 +406,7 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) + int len; + wchar_t *str = NULL; + +-#if !defined(__ANDROID__) && !defined(NO_ICONV) /* we don't use iconv on Android, or when it is explicitly disabled */ ++#if (!defined(__ANDROID__) || defined(__TERMUX__)) && !defined(NO_ICONV) /* we don't use iconv on non-Termux Android, or when it is explicitly disabled */ + wchar_t wbuf[256]; + /* iconv variables */ + iconv_t ic; +@@ -432,7 +432,7 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) + if (len < 2) /* we always skip first 2 bytes */ + return NULL; + +-#if defined(__ANDROID__) || defined(NO_ICONV) ++#if (defined(__ANDROID__) || defined(NO_ICONV)) && !defined(__TERMUX__) + + /* Bionic does not have iconv support nor wcsdup() function, so it + has to be done manually. The following code will only work for +-- +2.49.0 + diff --git a/packages/hidapi/0002-libusb-replace-some-functions-with-termux_usb-varian.patch b/packages/hidapi/0002-libusb-replace-some-functions-with-termux_usb-varian.patch new file mode 100644 index 000000000000000..10f3b7b4874804a --- /dev/null +++ b/packages/hidapi/0002-libusb-replace-some-functions-with-termux_usb-varian.patch @@ -0,0 +1,322 @@ +From d4188925aa23cd25d9b46225a9d74f960e34febf Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Wed, 5 Mar 2025 07:07:09 +0100 +Subject: [PATCH] libusb: replace some functions with termux_usb variants + +On Android we cannot use libusb_get_device_list and libusb_open +directly. Instead we need to enumerate and open devices through the +Android API. In Termux we have termux-api for handling this +communication, so we can replace the libusb_.. functions with +termux_usb_.. variants. + +Also set LIBUSB_OPTION_NO_DEVICE_DISCOVERY, it is required on android +as libusb otherwise errors early when trying to scan for devices. +--- + libusb/CMakeLists.txt | 2 + + libusb/hid.c | 205 +++++++++++++++++++++++------------------- + 2 files changed, 113 insertions(+), 94 deletions(-) + +diff --git a/libusb/CMakeLists.txt b/libusb/CMakeLists.txt +index beba0a3fdb33..7888de064e4e 100644 +--- a/libusb/CMakeLists.txt ++++ b/libusb/CMakeLists.txt +@@ -8,6 +8,8 @@ add_library(hidapi_libusb + ) + target_link_libraries(hidapi_libusb PUBLIC hidapi_include) + ++target_link_libraries(hidapi_libusb PRIVATE protobuf-c termux-usb) ++ + if(TARGET usb-1.0) + target_link_libraries(hidapi_libusb PRIVATE usb-1.0) + else() +diff --git a/libusb/hid.c b/libusb/hid.c +index d3861f772673..8e866f32de41 100644 +--- a/libusb/hid.c ++++ b/libusb/hid.c +@@ -50,6 +50,7 @@ + #endif + + #include "hidapi_libusb.h" ++#include "termux-usb.h" + + #if defined(__ANDROID__) && __ANDROID_API__ < __ANDROID_API_N__ + +@@ -541,6 +542,8 @@ int HID_API_EXPORT hid_init(void) + if (!usb_context) { + const char *locale; + ++ libusb_set_option(usb_context, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL); ++ + /* Init Libusb */ + if (libusb_init(&usb_context)) + return -1; +@@ -741,8 +744,8 @@ static uint16_t get_report_descriptor_size_from_interface_descriptors(const stru + + struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) + { +- libusb_device **devs; +- libusb_device *dev; ++ struct termux_usb_device **devices; ++ struct termux_usb_device *device; + libusb_device_handle *handle = NULL; + ssize_t num_devs; + int i = 0; +@@ -752,99 +755,102 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, + + if(hid_init() < 0) + return NULL; +- +- num_devs = libusb_get_device_list(usb_context, &devs); ++ num_devs = termux_usb_get_device_list(&devices); + if (num_devs < 0) + return NULL; +- while ((dev = devs[i++]) != NULL) { ++ while ((device = devices[i++]) != NULL) { + struct libusb_device_descriptor desc; +- struct libusb_config_descriptor *conf_desc = NULL; +- int j, k; ++ struct termux_usb_config_descriptor *conf_desc = NULL; ++ int j; + +- int res = libusb_get_device_descriptor(dev, &desc); +- unsigned short dev_vid = desc.idVendor; +- unsigned short dev_pid = desc.idProduct; ++ unsigned short dev_vid = device->device_descriptor->idVendor; ++ unsigned short dev_pid = device->device_descriptor->idProduct; + + if ((vendor_id != 0x0 && vendor_id != dev_vid) || + (product_id != 0x0 && product_id != dev_pid)) { + continue; + } + +- res = libusb_get_active_config_descriptor(dev, &conf_desc); +- if (res < 0) +- libusb_get_config_descriptor(dev, 0, &conf_desc); ++ termux_usb_get_config_descriptor(device, 0, &conf_desc); + if (conf_desc) { + for (j = 0; j < conf_desc->bNumInterfaces; j++) { +- const struct libusb_interface *intf = &conf_desc->interface[j]; +- for (k = 0; k < intf->num_altsetting; k++) { +- const struct libusb_interface_descriptor *intf_desc; +- intf_desc = &intf->altsetting[k]; +- if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { +- struct hid_device_info *tmp; ++ const struct termux_usb_interface_descriptor *intf_desc = &conf_desc->interface[j]; ++ if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { ++ struct hid_device_info *tmp; + +- res = libusb_open(dev, &handle); ++ intptr_t fd = termux_usb_open(device); ++ if (fd <= 0) { ++ LOG("Failed to open device\n"); ++ continue; ++ } ++ int res = libusb_wrap_sys_device(usb_context, fd, &handle); ++ if (res != 0) { ++ LOG("Failed to wrap sys device: %d\n", res); ++ continue; ++ } ++ libusb_device *dev = libusb_get_device(handle); + + #ifdef __ANDROID__ +- if (handle) { +- /* There is (a potential) libusb Android backend, in which +- device descriptor is not accurate up until the device is opened. +- https://github.com/libusb/libusb/pull/874#discussion_r632801373 +- A workaround is to re-read the descriptor again. +- Even if it is not going to be accepted into libusb master, +- having it here won't do any harm, since reading the device descriptor +- is as cheap as copy 18 bytes of data. */ +- libusb_get_device_descriptor(dev, &desc); +- } ++ if (handle) { ++ /* There is (a potential) libusb Android backend, in which ++ device descriptor is not accurate up until the device is opened. ++ https://github.com/libusb/libusb/pull/874#discussion_r632801373 ++ A workaround is to re-read the descriptor again. ++ Even if it is not going to be accepted into libusb master, ++ having it here won't do any harm, since reading the device descriptor ++ is as cheap as copy 18 bytes of data. */ ++ libusb_get_device_descriptor(dev, &desc); ++ } + #endif + +- tmp = create_device_info_for_device(dev, handle, &desc, conf_desc->bConfigurationValue, intf_desc->bInterfaceNumber); +- if (tmp) { ++ tmp = create_device_info_for_device(dev, handle, &desc, conf_desc->bConfigurationValue, intf_desc->bInterfaceNumber); ++ if (tmp) { + #ifdef INVASIVE_GET_USAGE +- /* TODO: have a runtime check for this section. */ +- +- /* +- This section is removed because it is too +- invasive on the system. Getting a Usage Page +- and Usage requires parsing the HID Report +- descriptor. Getting a HID Report descriptor +- involves claiming the interface. Claiming the +- interface involves detaching the kernel driver. +- Detaching the kernel driver is hard on the system +- because it will unclaim interfaces (if another +- app has them claimed) and the re-attachment of +- the driver will sometimes change /dev entry names. +- It is for these reasons that this section is +- optional. For composite devices, use the interface +- field in the hid_device_info struct to distinguish +- between interfaces. */ +- if (handle) { +- uint16_t report_descriptor_size = get_report_descriptor_size_from_interface_descriptors(intf_desc); +- +- invasive_fill_device_info_usage(tmp, handle, intf_desc->bInterfaceNumber, report_descriptor_size); +- } +-#endif /* INVASIVE_GET_USAGE */ ++ /* TODO: have a runtime check for this section. */ ++ ++ /* ++ This section is removed because it is too ++ invasive on the system. Getting a Usage Page ++ and Usage requires parsing the HID Report ++ descriptor. Getting a HID Report descriptor ++ involves claiming the interface. Claiming the ++ interface involves detaching the kernel driver. ++ Detaching the kernel driver is hard on the system ++ because it will unclaim interfaces (if another ++ app has them claimed) and the re-attachment of ++ the driver will sometimes change /dev entry names. ++ It is for these reasons that this section is ++ optional. For composite devices, use the interface ++ field in the hid_device_info struct to distinguish ++ between interfaces. */ ++ if (handle) { ++ uint16_t report_descriptor_size = get_report_descriptor_size_from_interface_descriptors(intf_desc); + +- if (cur_dev) { +- cur_dev->next = tmp; +- } +- else { +- root = tmp; +- } +- cur_dev = tmp; ++ invasive_fill_device_info_usage(tmp, handle, intf_desc->bInterfaceNumber, report_descriptor_size); + } ++#endif /* INVASIVE_GET_USAGE */ + +- if (res >= 0) { +- libusb_close(handle); +- handle = NULL; ++ if (cur_dev) { ++ cur_dev->next = tmp; + } ++ else { ++ root = tmp; ++ } ++ cur_dev = tmp; + } +- } /* altsettings */ ++ ++ if (res >= 0) { ++ libusb_close(handle); ++ handle = NULL; ++ } ++ break; ++ } + } /* interfaces */ +- libusb_free_config_descriptor(conf_desc); ++ termux_usb_free_config_descriptor(conf_desc); + } + } + +- libusb_free_device_list(devs, 1); ++ termux_usb_free_device_list(devices); + + return root; + } +@@ -1140,8 +1146,8 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) + { + hid_device *dev = NULL; + +- libusb_device **devs = NULL; +- libusb_device *usb_dev = NULL; ++ struct termux_usb_device **devs = NULL; ++ struct termux_usb_device *usb_dev = NULL; + int res = 0; + int d = 0; + int good_open = 0; +@@ -1151,40 +1157,51 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) + + dev = new_hid_device(); + +- libusb_get_device_list(usb_context, &devs); ++ termux_usb_get_device_list(&devs); + while ((usb_dev = devs[d++]) != NULL && !good_open) { +- struct libusb_config_descriptor *conf_desc = NULL; +- int j,k; ++ struct termux_usb_config_descriptor *conf_desc = NULL; ++ int j, k, i; + +- if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0) +- continue; ++ termux_usb_get_config_descriptor(usb_dev, 0, &conf_desc); + for (j = 0; j < conf_desc->bNumInterfaces && !good_open; j++) { +- const struct libusb_interface *intf = &conf_desc->interface[j]; +- for (k = 0; k < intf->num_altsetting && !good_open; k++) { +- const struct libusb_interface_descriptor *intf_desc = &intf->altsetting[k]; +- if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { +- char dev_path[64]; +- get_path(&dev_path, usb_dev, conf_desc->bConfigurationValue, intf_desc->bInterfaceNumber); +- if (!strcmp(dev_path, path)) { +- /* Matched Paths. Open this device */ +- +- /* OPEN HERE */ +- res = libusb_open(usb_dev, &dev->device_handle); +- if (res < 0) { +- LOG("can't open device\n"); +- break; ++ const struct termux_usb_interface_descriptor *intf = &conf_desc->interface[j]; ++ if (intf->bInterfaceClass == LIBUSB_CLASS_HID) { ++ if (!strcmp(usb_dev->device_address, path)) { ++ /* Matched Paths. Open this device */ ++ ++ /* OPEN HERE */ ++ intptr_t fd = termux_usb_open(usb_dev); ++ if (fd <= 0) { ++ LOG("Failed to open device\n"); ++ continue; ++ } ++ int res = libusb_wrap_sys_device(usb_context, fd, &dev->device_handle); ++ if (res != 0) { ++ LOG("Failed to wrap sys device: %d\n", res); ++ continue; ++ } ++ libusb_device *device = libusb_get_device(dev->device_handle); ++ ++ /* Now we can check actually get conf_desc and check altsetting */ ++ struct libusb_config_descriptor *libusb_conf_desc = NULL; ++ libusb_get_active_config_descriptor(device, &libusb_conf_desc); ++ for (k = 0; k < libusb_conf_desc->bNumInterfaces && !good_open; k++) { ++ const struct libusb_interface *libusb_intf = &libusb_conf_desc->interface[k]; ++ for (i = 0; i < libusb_intf->num_altsetting && !good_open; i++) { ++ const struct libusb_interface_descriptor *libusb_intf_desc = &libusb_intf->altsetting[k]; ++ if (libusb_intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) { ++ good_open = hidapi_initialize_device(dev, libusb_conf_desc->bConfigurationValue, libusb_intf_desc); ++ } + } +- good_open = hidapi_initialize_device(dev, conf_desc->bConfigurationValue, intf_desc); +- if (!good_open) +- libusb_close(dev->device_handle); + } ++ libusb_free_config_descriptor(libusb_conf_desc); + } + } + } +- libusb_free_config_descriptor(conf_desc); ++ termux_usb_free_config_descriptor(conf_desc); + } + +- libusb_free_device_list(devs, 1); ++ termux_usb_free_device_list(devs); + + /* If we have a good handle, return it. */ + if (good_open) { +-- +2.49.0 + diff --git a/packages/hidapi/0003-libusb-hid-set-path-to-device_address-from-termux-us.patch b/packages/hidapi/0003-libusb-hid-set-path-to-device_address-from-termux-us.patch new file mode 100644 index 000000000000000..4b1afd14ff4e706 --- /dev/null +++ b/packages/hidapi/0003-libusb-hid-set-path-to-device_address-from-termux-us.patch @@ -0,0 +1,48 @@ +From 36024a130df8bdbf64b7cb7f9c21dce4f8fe5530 Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Tue, 25 Mar 2025 14:16:01 +0100 +Subject: [PATCH] libusb: hid: set path to device_address from termux-usb + +The path normally comes from make_path/get_path, and is a string with +information about where/how device is connected and what configuration +and interface it uses. The information comes from +libusb_get_port_numbers, but that function does not seem to work on +android. Instead we can use the usbfs path to distinguish different +devices. + +This will likely break if a device has multiple configurations and +interfaces, and a program is suppose to use a specific one, but works +for simple devices and use-cases.. +--- + libusb/hid.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/libusb/hid.c b/libusb/hid.c +index 8e866f32de41..3f8d6a138448 100644 +--- a/libusb/hid.c ++++ b/libusb/hid.c +@@ -671,8 +671,6 @@ static struct hid_device_info * create_device_info_for_device(libusb_device *dev + + cur_dev->bus_type = HID_API_BUS_USB; + +- cur_dev->path = make_path(device, config_number, interface_num); +- + if (!handle) { + return cur_dev; + } +@@ -804,6 +802,12 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, + #endif + + tmp = create_device_info_for_device(dev, handle, &desc, conf_desc->bConfigurationValue, intf_desc->bInterfaceNumber); ++ /* path is suppose to come from make_path, but libusb_get_port_numbers ++ there does not seem to work on android. Instead pass and ++ distinguish devices based on the usbfs (/dev/bus/usb/001/001) ++ path. This likely has some drawbacks for devices with many ++ different configurations&interfaces.. */ ++ tmp->path = strdup(device->device_address); + if (tmp) { + #ifdef INVASIVE_GET_USAGE + /* TODO: have a runtime check for this section. */ +-- +2.49.0 + diff --git a/packages/hidapi/build.sh b/packages/hidapi/build.sh index 5a23cde2a98e7d5..29c898b995f2743 100644 --- a/packages/hidapi/build.sh +++ b/packages/hidapi/build.sh @@ -4,6 +4,11 @@ TERMUX_PKG_LICENSE="GPL-3.0, BSD 3-Clause, custom" TERMUX_PKG_LICENSE_FILE="LICENSE.txt, LICENSE-gpl3.txt, LICENSE-bsd.txt, LICENSE-orig.txt" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION=0.14.0 +TERMUX_PKG_REVISION=1 TERMUX_PKG_SRCURL="https://github.com/libusb/hidapi/archive/refs/tags/hidapi-${TERMUX_PKG_VERSION}.tar.gz" TERMUX_PKG_SHA256=a5714234abe6e1f53647dd8cba7d69f65f71c558b7896ed218864ffcf405bcbd -TERMUX_PKG_DEPENDS="libiconv, libusb" +TERMUX_PKG_DEPENDS="libiconv, libusb, libprotobuf-c, termux-api" + +termux_step_pre_configure() { + export CFLAGS+=" -DDEBUG_PRINTF=1" +} diff --git a/packages/hidapi/libusb-iconv-CMakeLists.txt.patch b/packages/hidapi/libusb-iconv-CMakeLists.txt.patch deleted file mode 100644 index 2a8261f04b22cb1..000000000000000 --- a/packages/hidapi/libusb-iconv-CMakeLists.txt.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- ./libusb/CMakeLists.txt~ 2025-02-01 13:16:49.669029267 +0000 -+++ ./libusb/CMakeLists.txt 2025-02-01 13:18:12.309030989 +0000 -@@ -22,7 +22,6 @@ - if(HIDAPI_NO_ICONV) - target_compile_definitions(hidapi_libusb PRIVATE NO_ICONV) - else() -- if(NOT ANDROID) - include(CheckCSourceCompiles) - - if(NOT CMAKE_VERSION VERSION_LESS 3.11) -@@ -77,9 +76,6 @@ - if(HIDAPI_ICONV_CONST) - target_compile_definitions(hidapi_libusb PRIVATE "ICONV_CONST=const") - endif() -- else() -- # On Android Iconv is disabled on the code level anyway, so no issue; -- endif() - endif() - - set_target_properties(hidapi_libusb diff --git a/packages/hidapi/termux-iconv-hid.c.patch b/packages/hidapi/termux-iconv-hid.c.patch deleted file mode 100644 index 42091e982ea3eb1..000000000000000 --- a/packages/hidapi/termux-iconv-hid.c.patch +++ /dev/null @@ -1,29 +0,0 @@ ---- ./libusb/hid.c~ 2023-05-22 10:50:06.000000000 +0000 -+++ ./libusb/hid.c 2025-02-01 13:13:05.412357928 +0000 -@@ -42,7 +42,7 @@ - - /* GNU / LibUSB */ - #include --#if !defined(__ANDROID__) && !defined(NO_ICONV) -+#if (!defined(__ANDROID__) || defined(__TERMUX__)) && !defined(NO_ICONV) - #include - #ifndef ICONV_CONST - #define ICONV_CONST -@@ -406,7 +406,7 @@ - int len; - wchar_t *str = NULL; - --#if !defined(__ANDROID__) && !defined(NO_ICONV) /* we don't use iconv on Android, or when it is explicitly disabled */ -+#if (!defined(__ANDROID__) || defined(__TERMUX__)) && !defined(NO_ICONV) /* we don't use iconv on non-Termux Android, or when it is explicitly disabled */ - wchar_t wbuf[256]; - /* iconv variables */ - iconv_t ic; -@@ -432,7 +432,7 @@ - if (len < 2) /* we always skip first 2 bytes */ - return NULL; - --#if defined(__ANDROID__) || defined(NO_ICONV) -+#if (defined(__ANDROID__) || defined(NO_ICONV)) && !defined(__TERMUX__) - - /* Bionic does not have iconv support nor wcsdup() function, so it - has to be done manually. The following code will only work for From cc2ec3532a5f2d36c2b1c2688380a2ca006a976d Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Sun, 16 Feb 2025 16:44:36 +0100 Subject: [PATCH 3/8] addpkg(main/python-hidapi): 0.14.0.post2 Required by electrum if one wants to access some hardware wallets. --- ...ld-only-libusb-backend-even-on-linux.patch | 64 +++++++++++++++++++ ...inst-iconv-termux-usb-and-protobuf-c.patch | 28 ++++++++ packages/python-hidapi/build.sh | 17 +++++ 3 files changed, 109 insertions(+) create mode 100644 packages/python-hidapi/0001-setup.py-build-only-libusb-backend-even-on-linux.patch create mode 100644 packages/python-hidapi/0002-termux-link-against-iconv-termux-usb-and-protobuf-c.patch create mode 100644 packages/python-hidapi/build.sh diff --git a/packages/python-hidapi/0001-setup.py-build-only-libusb-backend-even-on-linux.patch b/packages/python-hidapi/0001-setup.py-build-only-libusb-backend-even-on-linux.patch new file mode 100644 index 000000000000000..1808c4dd6af24d4 --- /dev/null +++ b/packages/python-hidapi/0001-setup.py-build-only-libusb-backend-even-on-linux.patch @@ -0,0 +1,64 @@ +From 79768217bb6bcd18e6f0dd00973ceff0d839798b Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Sun, 16 Feb 2025 19:43:58 +0100 +Subject: [PATCH] setup.py: build only libusb backend even on linux + +On android we cannot use hidraw, only libusb, so build only that +variant. +--- + setup.py | 18 +----------------- + 1 file changed, 1 insertion(+), 17 deletions(-) + +diff --git a/setup.py b/setup.py +index 7c79a38c7b8c..6e302ee12fd8 100755 +--- a/setup.py ++++ b/setup.py +@@ -137,7 +137,7 @@ def hid_from_embedded_hidapi(): + HIDAPI_WITH_LIBUSB = to_bool(os.getenv("HIDAPI_WITH_LIBUSB")) + + if HIDAPI_WITH_LIBUSB: +- hidraw_module = "hidraw" ++ hidraw_module = "hid" + modules.append( + pkgconfig_configure_extension( + Extension( +@@ -152,15 +152,6 @@ def hid_from_embedded_hidapi(): + hidraw_module = "hid" + check_deprecated_without_libusb() + +- modules.append( +- Extension( +- hidraw_module, +- sources=["hidraw.pyx", hidapi_src("linux")], +- include_dirs=[embedded_hidapi_include], +- libraries=["udev"], +- ) +- ) +- + else: + modules = [ + pkgconfig_configure_extension( +@@ -186,7 +177,6 @@ def hid_from_system_hidapi(): + HIDAPI_WITH_LIBUSB = to_bool(os.getenv("HIDAPI_WITH_LIBUSB")) + + if HIDAPI_WITH_LIBUSB: +- hidraw_module = "hidraw" + modules.append( + pkgconfig_configure_extension( + Extension("hid", sources=["hid.pyx"]), hidapi_libusb_pkgconfig +@@ -196,12 +186,6 @@ def hid_from_system_hidapi(): + hidraw_module = "hid" + check_deprecated_without_libusb() + +- modules.append( +- pkgconfig_configure_extension( +- Extension(hidraw_module, sources=["hidraw.pyx"]), +- hidapi_hidraw_pkgconfig, +- ) +- ) + else: + modules = [ + pkgconfig_configure_extension( +-- +2.49.0 + diff --git a/packages/python-hidapi/0002-termux-link-against-iconv-termux-usb-and-protobuf-c.patch b/packages/python-hidapi/0002-termux-link-against-iconv-termux-usb-and-protobuf-c.patch new file mode 100644 index 000000000000000..2961f34bd6c4a48 --- /dev/null +++ b/packages/python-hidapi/0002-termux-link-against-iconv-termux-usb-and-protobuf-c.patch @@ -0,0 +1,28 @@ +From 8390a9c52212a7df20987f4e39d05e7e44b638ef Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Sat, 8 Mar 2025 02:42:05 +0100 +Subject: [PATCH] termux: link against iconv, termux-usb and protobuf-c + +So that we can use termux-usb and termux-api's usb functions instead of (some of) +libusb's variants. +--- + setup.py | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/setup.py b/setup.py +index 6e302ee12fd8..ab6cf190ff41 100755 +--- a/setup.py ++++ b/setup.py +@@ -143,7 +143,8 @@ def hid_from_embedded_hidapi(): + Extension( + "hid", + sources=["hid.pyx", hidapi_src("libusb")], +- include_dirs=[embedded_hidapi_include], ++ include_dirs=[embedded_hidapi_include, "/data/data/com.termux/files/usr/include", "/data/data/com.termux/files/usr/include/libusb-1.0/"], ++ libraries=["iconv", "termux-usb", "protobuf-c"], + ), + libusb_pkgconfig, + ) +-- +2.49.0 + diff --git a/packages/python-hidapi/build.sh b/packages/python-hidapi/build.sh new file mode 100644 index 000000000000000..0fcb9ceca70cbc5 --- /dev/null +++ b/packages/python-hidapi/build.sh @@ -0,0 +1,17 @@ +TERMUX_PKG_HOMEPAGE=https://github.com/trezor/cython-hidapi +TERMUX_PKG_DESCRIPTION="Python wrapper for the HIDAPI" +TERMUX_PKG_LICENSE="GPL-3.0, BSD 3-Clause" +TERMUX_PKG_MAINTAINER="@termux" +TERMUX_PKG_VERSION=(0.14.0.post2 + 0.14.0) +TERMUX_PKG_SRCURL="https://github.com/trezor/cython-hidapi/archive/refs/tags/${TERMUX_PKG_VERSION}.tar.gz" +TERMUX_PKG_SHA256=72c51470f529f6e2f1b14a11c0220ed426af36efcd04dd1e58b97f3fb5702d74 +TERMUX_PKG_DEPENDS="hidapi, python, python-pip, termux-api, libprotobuf-c, libiconv" +TERMUX_PKG_PYTHON_COMMON_DEPS="Cython" +TERMUX_PKG_PLATFORM_INDEPENDENT=true +TERMUX_PKG_BUILD_IN_SRC=true + +termux_step_pre_configure() { + export HIDAPI_SYSTEM_HIDAPI=1 + export HIDAPI_WITH_LIBUSB=1 +} From b9ed1a03fb570aafe7469c8830e6b37dfa618b13 Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Sun, 16 Feb 2025 21:13:30 +0100 Subject: [PATCH 4/8] fix(main/electrum): fix text gui crash Fix two text gui crashes. --- ...001-termux-unset-all-android-checks.patch} | 59 ++++++++++++------- ...0002-termux-set-text-gui-as-default.patch} | 23 +++++++- ...03-gui-text-init-window-max_pos-to-0.patch | 45 ++++++++++++++ ...xt-set-fee_per_kb-to-0-if-it-is-None.patch | 45 ++++++++++++++ packages/electrum/build.sh | 2 +- 5 files changed, 149 insertions(+), 25 deletions(-) rename packages/electrum/{android.patch => 0001-termux-unset-all-android-checks.patch} (53%) rename packages/electrum/{only-text-gui.patch => 0002-termux-set-text-gui-as-default.patch} (50%) create mode 100644 packages/electrum/0003-gui-text-init-window-max_pos-to-0.patch create mode 100644 packages/electrum/0004-HACK-gui-text-set-fee_per_kb-to-0-if-it-is-None.patch diff --git a/packages/electrum/android.patch b/packages/electrum/0001-termux-unset-all-android-checks.patch similarity index 53% rename from packages/electrum/android.patch rename to packages/electrum/0001-termux-unset-all-android-checks.patch index 03f4b0d44ad7a98..100b4370d6363b9 100644 --- a/packages/electrum/android.patch +++ b/packages/electrum/0001-termux-unset-all-android-checks.patch @@ -1,17 +1,23 @@ ---- a/electrum/electrum -+++ b/electrum/electrum -@@ -42,7 +42,7 @@ - - script_dir = os.path.dirname(os.path.realpath(__file__)) - is_pyinstaller = getattr(sys, 'frozen', False) --is_android = 'ANDROID_DATA' in os.environ -+is_android = False - is_appimage = 'APPIMAGE' in os.environ - # is_local: unpacked tar.gz but not pip installed, or git clone - is_local = (not is_pyinstaller and not is_android and not is_appimage +From 03192e4805515d9d90469e11a151cc2088db55b0 Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Sun, 16 Feb 2025 20:48:02 +0100 +Subject: [PATCH] termux: unset all android checks + +Electrum expects android builds to be a "standard" java app. This will +not work in termux, as termux is more similar to a full linux +environment, so let's unset all the android checks. +--- + electrum/logging.py | 2 +- + electrum/simple_config.py | 2 +- + electrum/util.py | 8 ++++---- + run_electrum | 2 +- + 4 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/electrum/logging.py b/electrum/logging.py +index efe01f6c25cd..bf06d551f734 100644 --- a/electrum/logging.py +++ b/electrum/logging.py -@@ -354,7 +354,7 @@ +@@ -359,7 +359,7 @@ def get_logfile_path() -> Optional[pathlib.Path]: def describe_os_version() -> str: @@ -20,21 +26,25 @@ import jnius bv = jnius.autoclass('android.os.Build$VERSION') b = jnius.autoclass('android.os.Build') +diff --git a/electrum/simple_config.py b/electrum/simple_config.py +index 40ee656a5946..762fb447355d 100644 --- a/electrum/simple_config.py +++ b/electrum/simple_config.py -@@ -280,7 +280,7 @@ - def get_backup_dir(self): +@@ -436,7 +436,7 @@ class SimpleConfig(Logger): + def get_backup_dir(self) -> Optional[str]: # this is used to save wallet file backups (without active lightning channels) # on Android, the export backup button uses android_backup_dir() - if 'ANDROID_DATA' in os.environ: + if False: return None else: - return self.get('backup_dir') + return self.WALLET_BACKUP_DIRECTORY +diff --git a/electrum/util.py b/electrum/util.py +index a62abd86acac..597c061550b7 100644 --- a/electrum/util.py +++ b/electrum/util.py -@@ -386,7 +386,7 @@ - self.running = False +@@ -420,7 +420,7 @@ class DaemonThread(threading.Thread, Logger): + self.wake_up_event.clear() def on_stop(self): - if 'ANDROID_DATA' in os.environ: @@ -42,7 +52,7 @@ import jnius jnius.detach() self.logger.info("jnius detach") -@@ -519,7 +519,7 @@ +@@ -563,7 +563,7 @@ def get_new_wallet_name(wallet_folder: str) -> str: def is_android_debug_apk() -> bool: @@ -51,7 +61,7 @@ if not is_android: return False from jnius import autoclass -@@ -529,7 +529,7 @@ +@@ -573,7 +573,7 @@ def is_android_debug_apk() -> bool: def get_android_package_name() -> str: @@ -60,7 +70,7 @@ assert is_android from jnius import autoclass from android.config import ACTIVITY_CLASS_NAME -@@ -604,7 +604,7 @@ +@@ -637,7 +637,7 @@ def xor_bytes(a: bytes, b: bytes) -> bytes: def user_dir(): if "ELECTRUMDIR" in os.environ: return os.environ["ELECTRUMDIR"] @@ -69,14 +79,19 @@ return android_data_dir() elif os.name == 'posix': return os.path.join(os.environ["HOME"], ".electrum") +diff --git a/run_electrum b/run_electrum +index dc997981c2f8..6d60af24bf00 100755 --- a/run_electrum +++ b/run_electrum -@@ -42,7 +42,7 @@ +@@ -42,7 +42,7 @@ from typing import TYPE_CHECKING, Optional script_dir = os.path.dirname(os.path.realpath(__file__)) is_pyinstaller = getattr(sys, 'frozen', False) -is_android = 'ANDROID_DATA' in os.environ +is_android = False is_appimage = 'APPIMAGE' in os.environ + is_binary_distributable = is_pyinstaller or is_android or is_appimage # is_local: unpacked tar.gz but not pip installed, or git clone - is_local = (not is_pyinstaller and not is_android and not is_appimage +-- +2.48.1 + diff --git a/packages/electrum/only-text-gui.patch b/packages/electrum/0002-termux-set-text-gui-as-default.patch similarity index 50% rename from packages/electrum/only-text-gui.patch rename to packages/electrum/0002-termux-set-text-gui-as-default.patch index e4482d55be7e76c..c8d5bdcf3107895 100644 --- a/packages/electrum/only-text-gui.patch +++ b/packages/electrum/0002-termux-set-text-gui-as-default.patch @@ -1,6 +1,20 @@ +From c35fa0f1cbbf6ef3cb52b6192a3ee83741439a48 Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Sun, 16 Feb 2025 21:08:03 +0100 +Subject: [PATCH] termux: set text gui as default + +It is possible to run the other guis as well, with termux-x11 or vnc, +but default should be text gui in our terminal emulator. +--- + electrum/daemon.py | 2 +- + electrum/simple_config.py | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/electrum/daemon.py b/electrum/daemon.py +index ad7a81430271..6d8740307eb0 100644 --- a/electrum/daemon.py +++ b/electrum/daemon.py -@@ -606,7 +606,7 @@ +@@ -612,7 +612,7 @@ class Daemon(Logger): threading.current_thread().name = 'GUI' gui_name = self.config.GUI_NAME if gui_name in ['lite', 'classic']: @@ -9,9 +23,11 @@ self._plugins = Plugins(self.config, gui_name) # init plugins self.logger.info(f'launching GUI: {gui_name}') try: +diff --git a/electrum/simple_config.py b/electrum/simple_config.py +index 762fb447355d..d34e1ac21679 100644 --- a/electrum/simple_config.py +++ b/electrum/simple_config.py -@@ -1061,7 +1061,7 @@ +@@ -1080,7 +1080,7 @@ Warning: setting this to too low will result in lots of payment failures."""), RPC_SOCKET_TYPE = ConfigVar('rpcsock', default='auto', type_=str) RPC_SOCKET_FILEPATH = ConfigVar('rpcsockpath', default=None, type_=str) @@ -20,3 +36,6 @@ GUI_LAST_WALLET = ConfigVar('gui_last_wallet', default=None, type_=str) GUI_QT_COLOR_THEME = ConfigVar( +-- +2.48.1 + diff --git a/packages/electrum/0003-gui-text-init-window-max_pos-to-0.patch b/packages/electrum/0003-gui-text-init-window-max_pos-to-0.patch new file mode 100644 index 000000000000000..31d617d64133700 --- /dev/null +++ b/packages/electrum/0003-gui-text-init-window-max_pos-to-0.patch @@ -0,0 +1,45 @@ +From 90981e03ca14cb73aec0d5a1dc234d1f0551bca0 Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Sat, 15 Feb 2025 23:48:30 +0100 +Subject: [PATCH] gui: text: init window max_pos to 0 + +Otherwise running the text gui and directly pressing up or down +results in an error like: + + 2.45 | E | daemon.Daemon | GUI raised exception: AttributeError("'ElectrumGui' object has no attribute 'max_pos'"). shutting down. + 2.61 | E | __main__ | daemon.run_gui errored +Traceback (most recent call last): + File "/usr/bin/electrum", line 466, in handle_cmd + d.run_gui() + File "/usr/lib/python3.12/site-packages/electrum/daemon.py", line 598, in run_gui + self.gui_object.main() + File "/usr/lib/python3.12/site-packages/electrum/gui/text.py", line 537, in main + self.run_tab(0, self.print_history, self.run_history_tab) + File "/usr/lib/python3.12/site-packages/electrum/gui/text.py", line 473, in run_tab + c = self.main_command() + ^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.12/site-packages/electrum/gui/text.py", line 458, in main_command + self.increase_cursor(-1) + File "/usr/lib/python3.12/site-packages/electrum/gui/text.py", line 436, in increase_cursor + self.pos = min(self.pos, self.max_pos - 1) + ^^^^^^^^^^^^ +AttributeError: 'ElectrumGui' object has no attribute 'max_pos' +--- + electrum/gui/text.py | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/electrum/gui/text.py b/electrum/gui/text.py +index c5280f7a466b..73e374319ba0 100644 +--- a/electrum/gui/text.py ++++ b/electrum/gui/text.py +@@ -95,6 +95,7 @@ class ElectrumGui(BaseElectrumGui, EventListener): + self.lightning_invoice = None + self.tab = 0 + self.pos = 0 ++ self.max_pos = 0 + self.popup_pos = 0 + + self.str_recipient = "" +-- +2.48.1 + diff --git a/packages/electrum/0004-HACK-gui-text-set-fee_per_kb-to-0-if-it-is-None.patch b/packages/electrum/0004-HACK-gui-text-set-fee_per_kb-to-0-if-it-is-None.patch new file mode 100644 index 000000000000000..45227c0a2c44cef --- /dev/null +++ b/packages/electrum/0004-HACK-gui-text-set-fee_per_kb-to-0-if-it-is-None.patch @@ -0,0 +1,45 @@ +From 22fe19fa3e2a08146937b5d23bd742665c61cc71 Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Sat, 15 Feb 2025 23:51:47 +0100 +Subject: [PATCH] HACK: gui: text: set fee_per_kb to 0 if it is None + +To fix crash when settings tab is opened in text gui: + + 2.25 | E | daemon.Daemon | GUI raised exception: TypeError('conversion from NoneType to Decimal is not supported'). shutting down. + 2.59 | E | __main__ | daemon.run_gui errored +Traceback (most recent call last): + File "/usr/bin/electrum", line 466, in handle_cmd + d.run_gui() + File "/usr/lib/python3.12/site-packages/electrum/daemon.py", line 598, in run_gui + self.gui_object.main() + File "/usr/lib/python3.12/site-packages/electrum/gui/text.py", line 537, in main + self.run_tab(0, self.print_history, self.run_history_tab) + File "/usr/lib/python3.12/site-packages/electrum/gui/text.py", line 473, in run_tab + c = self.main_command() + ^^^^^^^^^^^^^^^^^^^ + File "/usr/lib/python3.12/site-packages/electrum/gui/text.py", line 464, in main_command + self.settings_dialog() + File "/usr/lib/python3.12/site-packages/electrum/gui/text.py", line 763, in settings_dialog + fee = str(Decimal(self.config.fee_per_kb()) / COIN) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +TypeError: conversion from NoneType to Decimal is not supported +--- + electrum/gui/text.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/electrum/gui/text.py b/electrum/gui/text.py +index 73e374319ba0..cffc4230ebf2 100644 +--- a/electrum/gui/text.py ++++ b/electrum/gui/text.py +@@ -761,7 +761,7 @@ class ElectrumGui(BaseElectrumGui, EventListener): + self.network.run_from_another_thread(self.network.set_parameters(net_params)) + + def settings_dialog(self): +- fee = str(Decimal(self.config.fee_per_kb()) / COIN) ++ fee = str(Decimal(self.config.fee_per_kb() if self.config.fee_per_kb() else 0) / COIN) + out = self.run_dialog('Settings', [ + {'label':'Default fee', 'type':'satoshis', 'value': fee} + ], buttons = 1) +-- +2.48.1 + diff --git a/packages/electrum/build.sh b/packages/electrum/build.sh index 4a00b7d6027b104..8046cedf136e2a5 100644 --- a/packages/electrum/build.sh +++ b/packages/electrum/build.sh @@ -3,7 +3,7 @@ TERMUX_PKG_DESCRIPTION="Electrum is a lightweight Bitcoin wallet" TERMUX_PKG_LICENSE="MIT" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION="4.5.8" -TERMUX_PKG_REVISION=2 +TERMUX_PKG_REVISION=3 TERMUX_PKG_SRCURL=https://download.electrum.org/$TERMUX_PKG_VERSION/Electrum-$TERMUX_PKG_VERSION.tar.gz TERMUX_PKG_SHA256=dd8595a138132dee87cee76ce760a1d622fc2fd65d3b6ac7df7e53b7fb6ea7e8 # The python dependency list should be compared to From 1d84a623b4b9a6c9484c6fa6510f9e14d86715e2 Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Sun, 16 Feb 2025 16:24:42 +0100 Subject: [PATCH 5/8] addpkg(main/python-btchip): 0.1.32 The package is EOL, and should hence not be welcome in our repositories. It is however required by electrum (to support legacy hardware keys), and it is no longer possible to install it through pip without an extra patch, so let's make an exception and add it to our repositories. Hopefully electrum can be fixed so that this dependency is optional. --- ...ghtly-newer-dependency-for-python-py.patch | 31 +++++++++++++++++++ packages/python-btchip/build.sh | 22 +++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 packages/python-btchip/0001-setup.py-use-slightly-newer-dependency-for-python-py.patch create mode 100644 packages/python-btchip/build.sh diff --git a/packages/python-btchip/0001-setup.py-use-slightly-newer-dependency-for-python-py.patch b/packages/python-btchip/0001-setup.py-use-slightly-newer-dependency-for-python-py.patch new file mode 100644 index 000000000000000..3f1f9567791ee40 --- /dev/null +++ b/packages/python-btchip/0001-setup.py-use-slightly-newer-dependency-for-python-py.patch @@ -0,0 +1,31 @@ +From f1c4274af869265d07a520d527cce33a82e1a3b9 Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Sun, 16 Feb 2025 16:13:42 +0100 +Subject: [PATCH] setup.py: use (slightly) newer dependency for python-pyscard + +1.6.12-4build1 is not a valid version string, and gives an error in +modern python versions: + + error in btchip-python setup command: 'extras_require' must be a dictionary whose values are strings or lists of strings containing valid project/version requirement specifiers. + +Use the next available python-pyscard as min version. +--- + setup.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/setup.py b/setup.py +index accfe2485778..5be3c9ebc45b 100644 +--- a/setup.py ++++ b/setup.py +@@ -17,7 +17,7 @@ setup( + packages=find_packages(), + install_requires=['hidapi>=0.7.99', 'ecdsa>=0.9'], + extras_require = { +- 'smartcard': [ 'python-pyscard>=1.6.12-4build1' ] ++ 'smartcard': [ 'python-pyscard>=1.6.14' ] + }, + include_package_data=True, + zip_safe=False, +-- +2.48.1 + diff --git a/packages/python-btchip/build.sh b/packages/python-btchip/build.sh new file mode 100644 index 000000000000000..17fe87544771eec --- /dev/null +++ b/packages/python-btchip/build.sh @@ -0,0 +1,22 @@ +TERMUX_PKG_HOMEPAGE=https://github.com/LedgerHQ/btchip-python +TERMUX_PKG_DESCRIPTION="Ledger HW.1 Python API" +TERMUX_PKG_LICENSE="Apache-2.0" +TERMUX_PKG_MAINTAINER="@termux" +TERMUX_PKG_VERSION=0.1.32 +TERMUX_PKG_SRCURL="https://github.com/LedgerHQ/btchip-python/archive/refs/tags/${TERMUX_PKG_VERSION}.tar.gz" +TERMUX_PKG_SHA256=b5cce374072467d27267d5c4a9159e7e956a016543d8a0f00e20c9830061ec9b +# EOL, no need to check for updates +TERMUX_PKG_AUTO_UPDATE=false +TERMUX_PKG_DEPENDS="python, python-pip, python-hidapi" +TERMUX_PKG_PYTHON_COMMON_DEPS="wheel" +TERMUX_PKG_PYTHON_TARGET_DEPS="ecdsa" +TERMUX_PKG_PLATFORM_INDEPENDENT=true +TERMUX_PKG_BUILD_IN_SRC=true + +termux_step_create_debscripts() { + cat <<- EOF > ./postinst + #!$TERMUX_PREFIX/bin/sh + echo "Installing dependencies through pip..." + pip3 install ${TERMUX_PKG_PYTHON_TARGET_DEPS//, / } + EOF +} From e453bf44ecc270fd200d540b490eccad63602f3f Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Sun, 16 Feb 2025 21:17:32 +0100 Subject: [PATCH 6/8] enhance(main/electrum): suggest packages python-{btchip,hidapi} If a ledger hardware wallet is to be used with electrum, then these dependencies are needed. --- ...0001-termux-unset-all-android-checks.patch | 125 +++++++++++++++++- .../0002-termux-set-text-gui-as-default.patch | 4 +- ...03-gui-text-init-window-max_pos-to-0.patch | 4 +- ...xt-set-fee_per_kb-to-0-if-it-is-None.patch | 4 +- packages/electrum/build.sh | 3 +- 5 files changed, 126 insertions(+), 14 deletions(-) diff --git a/packages/electrum/0001-termux-unset-all-android-checks.patch b/packages/electrum/0001-termux-unset-all-android-checks.patch index 100b4370d6363b9..4e883e0a566afc3 100644 --- a/packages/electrum/0001-termux-unset-all-android-checks.patch +++ b/packages/electrum/0001-termux-unset-all-android-checks.patch @@ -1,4 +1,4 @@ -From 03192e4805515d9d90469e11a151cc2088db55b0 Mon Sep 17 00:00:00 2001 +From f63d200b667ac3438816479e82a69b5825201e4c Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Sun, 16 Feb 2025 20:48:02 +0100 Subject: [PATCH] termux: unset all android checks @@ -7,12 +7,110 @@ Electrum expects android builds to be a "standard" java app. This will not work in termux, as termux is more similar to a full linux environment, so let's unset all the android checks. --- - electrum/logging.py | 2 +- - electrum/simple_config.py | 2 +- - electrum/util.py | 8 ++++---- - run_electrum | 2 +- - 4 files changed, 7 insertions(+), 7 deletions(-) + electrum/ecc_fast.py | 2 +- + electrum/electrum | 2 +- + electrum/gui/default_lang.py | 2 +- + electrum/gui/qml/qeapp.py | 4 ++-- + electrum/gui/qml/qeqrscanner.py | 6 +++--- + electrum/logging.py | 2 +- + electrum/qrreader/zbar.py | 2 +- + electrum/simple_config.py | 2 +- + electrum/util.py | 8 ++++---- + run_electrum | 2 +- + 10 files changed, 16 insertions(+), 16 deletions(-) +diff --git a/electrum/ecc_fast.py b/electrum/ecc_fast.py +index a27e2114961f..b88d9acf7dc2 100644 +--- a/electrum/ecc_fast.py ++++ b/electrum/ecc_fast.py +@@ -47,7 +47,7 @@ def load_library(): + elif sys.platform in ('windows', 'win32'): + for v in tested_libversions: + libnames.append(f"libsecp256k1-{v}.dll") +- elif 'ANDROID_DATA' in os.environ: ++ elif False: + libnames = ['libsecp256k1.so', ] # don't care about version number. we built w/e is available. + else: # desktop Linux and similar + for v in tested_libversions: +diff --git a/electrum/electrum b/electrum/electrum +index dc997981c2f8..6d60af24bf00 100755 +--- a/electrum/electrum ++++ b/electrum/electrum +@@ -42,7 +42,7 @@ from typing import TYPE_CHECKING, Optional + + script_dir = os.path.dirname(os.path.realpath(__file__)) + is_pyinstaller = getattr(sys, 'frozen', False) +-is_android = 'ANDROID_DATA' in os.environ ++is_android = False + is_appimage = 'APPIMAGE' in os.environ + is_binary_distributable = is_pyinstaller or is_android or is_appimage + # is_local: unpacked tar.gz but not pip installed, or git clone +diff --git a/electrum/gui/default_lang.py b/electrum/gui/default_lang.py +index aacf8112f05e..c8c1d07afb74 100644 +--- a/electrum/gui/default_lang.py ++++ b/electrum/gui/default_lang.py +@@ -13,7 +13,7 @@ from electrum.i18n import languages + + + jLocale = None +-if "ANDROID_DATA" in os.environ: ++if False: + from jnius import autoclass, cast + jLocale = autoclass("java.util.Locale") + +diff --git a/electrum/gui/qml/qeapp.py b/electrum/gui/qml/qeapp.py +index 87f5ad7a6e17..4df3dee1faa2 100644 +--- a/electrum/gui/qml/qeapp.py ++++ b/electrum/gui/qml/qeapp.py +@@ -49,7 +49,7 @@ if TYPE_CHECKING: + from electrum.daemon import Daemon + from electrum.plugin import Plugins + +-if 'ANDROID_DATA' in os.environ: ++if False: + from jnius import autoclass, cast + from android import activity + +@@ -281,7 +281,7 @@ class QEAppController(BaseCrashReporter, QObject): + + @pyqtSlot(result=bool) + def isAndroid(self): +- return 'ANDROID_DATA' in os.environ ++ return False + + @pyqtSlot(result='QVariantMap') + def crashData(self): +diff --git a/electrum/gui/qml/qeqrscanner.py b/electrum/gui/qml/qeqrscanner.py +index a301cdaa93f5..712d5dadec88 100644 +--- a/electrum/gui/qml/qeqrscanner.py ++++ b/electrum/gui/qml/qeqrscanner.py +@@ -9,7 +9,7 @@ from electrum.logging import get_logger + from electrum.i18n import _ + + +-if 'ANDROID_DATA' in os.environ: ++if False: + from jnius import autoclass + from android import activity + +@@ -54,7 +54,7 @@ class QEQRScanner(QObject): + + @pyqtSlot() + def open(self): +- if 'ANDROID_DATA' not in os.environ: ++ if True: + self._scan_qr_non_android() + return + jSimpleScannerActivity = autoclass("org.electrum.qr.SimpleScannerActivity") +@@ -77,7 +77,7 @@ class QEQRScanner(QObject): + + @pyqtSlot() + def _unbind(self): +- if 'ANDROID_DATA' in os.environ: ++ if False: + activity.unbind(on_activity_result=self.on_qr_activity_result) + + def _scan_qr_non_android(self): diff --git a/electrum/logging.py b/electrum/logging.py index efe01f6c25cd..bf06d551f734 100644 --- a/electrum/logging.py @@ -26,6 +124,19 @@ index efe01f6c25cd..bf06d551f734 100644 import jnius bv = jnius.autoclass('android.os.Build$VERSION') b = jnius.autoclass('android.os.Build') +diff --git a/electrum/qrreader/zbar.py b/electrum/qrreader/zbar.py +index 8a3ef54dfdf8..73144caf2366 100644 +--- a/electrum/qrreader/zbar.py ++++ b/electrum/qrreader/zbar.py +@@ -37,7 +37,7 @@ from .abstract_base import AbstractQrCodeReader, QrCodeResult + + _logger = get_logger(__name__) + +-if 'ANDROID_DATA' in os.environ: ++if False: + LIBNAME = 'libzbar.so' + elif sys.platform == 'darwin': + LIBNAME = 'libzbar.0.dylib' diff --git a/electrum/simple_config.py b/electrum/simple_config.py index 40ee656a5946..762fb447355d 100644 --- a/electrum/simple_config.py @@ -93,5 +204,5 @@ index dc997981c2f8..6d60af24bf00 100755 is_binary_distributable = is_pyinstaller or is_android or is_appimage # is_local: unpacked tar.gz but not pip installed, or git clone -- -2.48.1 +2.49.0 diff --git a/packages/electrum/0002-termux-set-text-gui-as-default.patch b/packages/electrum/0002-termux-set-text-gui-as-default.patch index c8d5bdcf3107895..b13d496c22b3b90 100644 --- a/packages/electrum/0002-termux-set-text-gui-as-default.patch +++ b/packages/electrum/0002-termux-set-text-gui-as-default.patch @@ -1,4 +1,4 @@ -From c35fa0f1cbbf6ef3cb52b6192a3ee83741439a48 Mon Sep 17 00:00:00 2001 +From ad45206125a21fc4324e9f4787fffeabe944df97 Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Sun, 16 Feb 2025 21:08:03 +0100 Subject: [PATCH] termux: set text gui as default @@ -37,5 +37,5 @@ index 762fb447355d..d34e1ac21679 100644 GUI_QT_COLOR_THEME = ConfigVar( -- -2.48.1 +2.49.0 diff --git a/packages/electrum/0003-gui-text-init-window-max_pos-to-0.patch b/packages/electrum/0003-gui-text-init-window-max_pos-to-0.patch index 31d617d64133700..51f6c7ac9463b1d 100644 --- a/packages/electrum/0003-gui-text-init-window-max_pos-to-0.patch +++ b/packages/electrum/0003-gui-text-init-window-max_pos-to-0.patch @@ -1,4 +1,4 @@ -From 90981e03ca14cb73aec0d5a1dc234d1f0551bca0 Mon Sep 17 00:00:00 2001 +From 7f497724258ab3efa567dcbd9fb424b22f5dc92a Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Sat, 15 Feb 2025 23:48:30 +0100 Subject: [PATCH] gui: text: init window max_pos to 0 @@ -41,5 +41,5 @@ index c5280f7a466b..73e374319ba0 100644 self.str_recipient = "" -- -2.48.1 +2.49.0 diff --git a/packages/electrum/0004-HACK-gui-text-set-fee_per_kb-to-0-if-it-is-None.patch b/packages/electrum/0004-HACK-gui-text-set-fee_per_kb-to-0-if-it-is-None.patch index 45227c0a2c44cef..802d307154e85e0 100644 --- a/packages/electrum/0004-HACK-gui-text-set-fee_per_kb-to-0-if-it-is-None.patch +++ b/packages/electrum/0004-HACK-gui-text-set-fee_per_kb-to-0-if-it-is-None.patch @@ -1,4 +1,4 @@ -From 22fe19fa3e2a08146937b5d23bd742665c61cc71 Mon Sep 17 00:00:00 2001 +From f7ef5d2aad1e7c3e9b4745bb50c6babf5faeca6b Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Sat, 15 Feb 2025 23:51:47 +0100 Subject: [PATCH] HACK: gui: text: set fee_per_kb to 0 if it is None @@ -41,5 +41,5 @@ index 73e374319ba0..cffc4230ebf2 100644 {'label':'Default fee', 'type':'satoshis', 'value': fee} ], buttons = 1) -- -2.48.1 +2.49.0 diff --git a/packages/electrum/build.sh b/packages/electrum/build.sh index 8046cedf136e2a5..cfcd9f51a80b45d 100644 --- a/packages/electrum/build.sh +++ b/packages/electrum/build.sh @@ -11,7 +11,8 @@ TERMUX_PKG_SHA256=dd8595a138132dee87cee76ce760a1d622fc2fd65d3b6ac7df7e53b7fb6ea7 # update (or at least every major update). Disable auto updates for # now. TERMUX_PKG_AUTO_UPDATE=false -TERMUX_PKG_DEPENDS="python, libsecp256k1, python-pip, python-cryptography" +TERMUX_PKG_DEPENDS="libsecp256k1, python, python-cryptography, python-pip" +TERMUX_PKG_SUGGESTS="python-btchip, python-hidapi" TERMUX_PKG_PYTHON_TARGET_DEPS="'qrcode', 'protobuf<4,>=3.20', 'qdarkstyle>=2.7', 'aiorpcx<0.24,>=0.22.0', 'aiohttp<4.0.0,>=3.3.0', 'aiohttp_socks>=0.8.4', 'certifi', 'attrs>=20.1.0', 'jsonpatch', 'dnspython>=2.0'" TERMUX_PKG_BUILD_IN_SRC=true TERMUX_PKG_PLATFORM_INDEPENDENT=true From 380f1fd7f3a02bf25af77aa5be28b165f2e40d0a Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Thu, 3 Apr 2025 18:35:42 +0200 Subject: [PATCH 7/8] fix(main/electrum): look for just libsecp256k1.so Instead of libsecp256k1.so.1 or libsecp256k1.so.2. Termux's libsecp256k1 package provides the library without version suffix. --- ...05-ecc_fast-look-for-libsecp256k1.so.patch | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 packages/electrum/0005-ecc_fast-look-for-libsecp256k1.so.patch diff --git a/packages/electrum/0005-ecc_fast-look-for-libsecp256k1.so.patch b/packages/electrum/0005-ecc_fast-look-for-libsecp256k1.so.patch new file mode 100644 index 000000000000000..8bdcbd028b02954 --- /dev/null +++ b/packages/electrum/0005-ecc_fast-look-for-libsecp256k1.so.patch @@ -0,0 +1,27 @@ +From c6832bcf17376f9d3bac907db7342e8a7c3fff9a Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Thu, 3 Apr 2025 18:29:51 +0200 +Subject: [PATCH] ecc_fast: look for libsecp256k1.so + +Without .1 or .2 version suffix. Termux's libsecp256k1 library is +called just libsecp256k1.so. +--- + electrum/ecc_fast.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/electrum/ecc_fast.py b/electrum/ecc_fast.py +index b88d9acf7dc2..35b14aa62587 100644 +--- a/electrum/ecc_fast.py ++++ b/electrum/ecc_fast.py +@@ -47,7 +47,7 @@ def load_library(): + elif sys.platform in ('windows', 'win32'): + for v in tested_libversions: + libnames.append(f"libsecp256k1-{v}.dll") +- elif False: ++ elif True: + libnames = ['libsecp256k1.so', ] # don't care about version number. we built w/e is available. + else: # desktop Linux and similar + for v in tested_libversions: +-- +2.49.0 + From bd6af5641032744c1aa1f10c3be93def095a1072 Mon Sep 17 00:00:00 2001 From: Henrik Grimler Date: Wed, 26 Feb 2025 07:06:23 +0100 Subject: [PATCH 8/8] addpkg(main/heimdall): 2.1.0 Add patches to use termux-api/termux-usb to enumerate and open devices. --- ...mux_usb-functions-instead-of-some-li.patch | 201 ++++++++++++++++++ packages/heimdall/build.sh | 13 ++ 2 files changed, 214 insertions(+) create mode 100644 packages/heimdall/0001-heimdall-use-termux_usb-functions-instead-of-some-li.patch create mode 100644 packages/heimdall/build.sh diff --git a/packages/heimdall/0001-heimdall-use-termux_usb-functions-instead-of-some-li.patch b/packages/heimdall/0001-heimdall-use-termux_usb-functions-instead-of-some-li.patch new file mode 100644 index 000000000000000..2149703d1119579 --- /dev/null +++ b/packages/heimdall/0001-heimdall-use-termux_usb-functions-instead-of-some-li.patch @@ -0,0 +1,201 @@ +From 9d56f62f268fde4c6a421cf5b1b3c5ba587fdd60 Mon Sep 17 00:00:00 2001 +From: Henrik Grimler +Date: Wed, 26 Feb 2025 00:43:59 +0100 +Subject: [PATCH] heimdall: use termux_usb functions instead of (some) libusb + ones + +The functions are provided by libtermux-usb, which is part of +termux-api. +--- + heimdall/CMakeLists.txt | 5 +- + heimdall/source/BridgeManager.cpp | 79 ++++++++++++++++++++----------- + 2 files changed, 55 insertions(+), 29 deletions(-) + +diff --git a/heimdall/CMakeLists.txt b/heimdall/CMakeLists.txt +index 0cb0e2c0c42e..12c32dadb043 100644 +--- a/heimdall/CMakeLists.txt ++++ b/heimdall/CMakeLists.txt +@@ -37,7 +37,8 @@ set(HEIMDALL_SOURCE_FILES + source/main.cpp + source/PrintPitAction.cpp + source/Utility.cpp +- source/VersionAction.cpp) ++ source/VersionAction.cpp ++) + + include(CheckSymbolExists) + +@@ -74,7 +75,7 @@ add_executable(heimdall ${HEIMDALL_SOURCE_FILES}) + target_compile_features(heimdall PRIVATE cxx_std_11) + + target_link_libraries(heimdall PRIVATE pit) +-target_link_libraries(heimdall PRIVATE ${LIBUSB_LIBRARIES}) ++target_link_libraries(heimdall PRIVATE ${LIBUSB_LIBRARIES} termux-usb protobuf-c) + install (TARGETS heimdall + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) +diff --git a/heimdall/source/BridgeManager.cpp b/heimdall/source/BridgeManager.cpp +index f2ceee9e2cbe..45cdff8fb3f2 100644 +--- a/heimdall/source/BridgeManager.cpp ++++ b/heimdall/source/BridgeManager.cpp +@@ -25,6 +25,8 @@ + // libusb + #include + ++#include "termux-usb.h" ++ + // Heimdall + #include "BeginDumpPacket.h" + #include "BeginSessionPacket.h" +@@ -83,50 +85,64 @@ int BridgeManager::FindDeviceInterface(void) + else + Interface::Print("Detecting device...\n"); + +- struct libusb_device **devices; ++ struct termux_usb_device **devices; ++ char heimdallDeviceAddress[PATH_MAX] = {'\0'}; + unsigned int deviceCount, deviceIndex, i; +- libusb_device_descriptor descriptor; + while (true) + { +- deviceCount = libusb_get_device_list(libusbContext, &devices); ++ deviceCount = termux_usb_get_device_list(&devices); + + for (deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) + { +- libusb_get_device_descriptor(devices[deviceIndex], &descriptor); +- + for (i = 0; i < BridgeManager::kSupportedDeviceCount; i++) + { +- if (descriptor.idVendor == supportedDevices[i].vendorId && +- descriptor.idProduct == supportedDevices[i].productId) ++ if (devices[deviceIndex]->device_descriptor->idVendor == supportedDevices[i].vendorId && ++ devices[deviceIndex]->device_descriptor->idProduct == supportedDevices[i].productId) + { +- heimdallDevice = devices[deviceIndex]; +- libusb_ref_device(heimdallDevice); ++ strncpy(heimdallDeviceAddress, devices[deviceIndex]->device_address, PATH_MAX-1); ++ heimdallDeviceAddress[PATH_MAX-1] = '\0'; ++ Interface::Print("Found device\n"); + break; + } + } + } +- if (heimdallDevice) ++ if (heimdallDeviceAddress[0] != '\0') + break; + +- libusb_free_device_list(devices, deviceCount); ++ termux_usb_free_device_list(devices); + if (waitForDevice) + Sleep(1000); + else + break; + } + +- if (!heimdallDevice) ++ if (heimdallDeviceAddress[0] == '\0') + { + Interface::PrintDeviceDetectionFailed(); + return (BridgeManager::kInitialiseDeviceNotDetected); + } + +- int result = libusb_open(heimdallDevice, &deviceHandle); +- if (result != LIBUSB_SUCCESS) +- { +- Interface::PrintError("Failed to access device. libusb error: %d\n", result); ++ intptr_t fd = termux_usb_open_address(heimdallDeviceAddress); ++ if (fd <= 0) { ++ Interface::PrintError("termux_usb_open failed\n"); + return (BridgeManager::kInitialiseFailed); + } ++ int result = libusb_wrap_sys_device(libusbContext, fd, &deviceHandle); ++ if (result != LIBUSB_SUCCESS) { ++ Interface::PrintError("libusb_wrap_sys_device failed. libusb error: %d\n", result); ++ return (BridgeManager::kInitialiseFailed); ++ } else if (deviceHandle == NULL) { ++ Interface::PrintError("libusb_wrap_sys_device returned an invalid handle\n"); ++ return (BridgeManager::kInitialiseFailed); ++ } ++ heimdallDevice = libusb_get_device(deviceHandle); ++ libusb_ref_device(heimdallDevice); ++ ++ if (!heimdallDevice) ++ { ++ Interface::PrintDeviceDetectionFailed(); ++ return (BridgeManager::kInitialiseDeviceNotDetected); ++ } + + libusb_device_descriptor deviceDescriptor; + result = libusb_get_device_descriptor(heimdallDevice, &deviceDescriptor); +@@ -418,7 +434,13 @@ BridgeManager::~BridgeManager() + bool BridgeManager::DetectDevice(void) + { + // Initialise libusb +- int result = libusb_init(&libusbContext); ++ int result; ++ result = libusb_set_option(libusbContext, LIBUSB_OPTION_WEAK_AUTHORITY, NULL); ++ if (result != LIBUSB_SUCCESS) { ++ Interface::PrintError("libusb_set_option failed: %d\n", result); ++ return (false); ++ } ++ result = libusb_init(&libusbContext); + + if (result != LIBUSB_SUCCESS) + { +@@ -432,23 +454,20 @@ bool BridgeManager::DetectDevice(void) + // Set libusb log level. + SetUsbLogLevel(usbLogLevel); + +- struct libusb_device **devices; ++ struct termux_usb_device **devices; + unsigned int deviceCount, deviceIndex, i; +- libusb_device_descriptor descriptor; + while (true) + { +- deviceCount = libusb_get_device_list(libusbContext, &devices); ++ deviceCount = termux_usb_get_device_list(&devices); + + for (deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) + { +- libusb_get_device_descriptor(devices[deviceIndex], &descriptor); +- + for (i = 0; i < BridgeManager::kSupportedDeviceCount; i++) + { +- if (descriptor.idVendor == supportedDevices[i].vendorId && +- descriptor.idProduct == supportedDevices[i].productId) ++ if (devices[deviceIndex]->device_descriptor->idVendor == supportedDevices[i].vendorId && ++ devices[deviceIndex]->device_descriptor->idProduct == supportedDevices[i].productId) + { +- libusb_free_device_list(devices, deviceCount); ++ termux_usb_free_device_list(devices); + + Interface::Print("Device detected\n"); + return (true); +@@ -456,7 +475,7 @@ bool BridgeManager::DetectDevice(void) + } + } + +- libusb_free_device_list(devices, deviceCount); ++ termux_usb_free_device_list(devices); + if (waitForDevice) + Sleep(1000); + else +@@ -471,7 +490,13 @@ int BridgeManager::Initialise(bool resume) + Interface::Print("Initialising connection...\n"); + + // Initialise libusb +- int result = libusb_init(&libusbContext); ++ int result; ++ result = libusb_set_option(libusbContext, LIBUSB_OPTION_WEAK_AUTHORITY, NULL); ++ if (result != LIBUSB_SUCCESS) { ++ Interface::PrintError("libusb_set_option failed: %d\n", result); ++ return (false); ++ } ++ result = libusb_init(&libusbContext); + + if (result != LIBUSB_SUCCESS) + { +-- +2.49.0 + diff --git a/packages/heimdall/build.sh b/packages/heimdall/build.sh new file mode 100644 index 000000000000000..02cbff93c344cc3 --- /dev/null +++ b/packages/heimdall/build.sh @@ -0,0 +1,13 @@ +TERMUX_PKG_HOMEPAGE=https://git.sr.ht/~grimler/Heimdall +TERMUX_PKG_DESCRIPTION="Tool for flashing firmware onto Samsung Galaxy devices" +TERMUX_PKG_LICENSE="MIT" +TERMUX_PKG_MAINTAINER="Henrik Grimler @grimler91" +TERMUX_PKG_VERSION=2.1.0 +TERMUX_PKG_SRCURL="https://git.sr.ht/~grimler/Heimdall/archive/v${TERMUX_PKG_VERSION}.tar.gz" +TERMUX_PKG_SHA256=51276d09acb14cdd7cfca4547ccfa059fa7a4d43fb741afd618c495996a50bfe +TERMUX_PKG_DEPENDS="libprotobuf-c, libusb, termux-api" +TERMUX_PKG_EXTRA_CONFIGURE_ARGS="-DDISABLE_FRONTEND=1" + +termux_step_pre_configure() { + termux_setup_protobuf +}