From 3b31ee881b67fa78d4782aafe24acfca63db2764 Mon Sep 17 00:00:00 2001 From: Mohamed Gaber Date: Thu, 20 Apr 2023 14:55:37 -0700 Subject: [PATCH] Move to HID usage-page-based detection (#46) --- .github/ISSUE_TEMPLATE/bug_report.yml | 35 ++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/requests.yml | 10 +++++++ .github/workflows/ci.yml | 1 + .gitignore | 5 +++- CMakeLists.txt | 11 ++++---- Changelog.md | 6 +++++ Dockerfile | 2 +- Readme.md | 8 +++--- lib/nuphy.cpp | 36 ++++++++++++++++++++++--- package.json | 12 +++------ shell.nix | 5 ++-- src/main.cpp | 13 +++++++-- yarn.lock | 38 ++++++++++++++++----------- 13 files changed, 137 insertions(+), 45 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/requests.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..a8d663b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,35 @@ +name: Bug Report +description: If Nudelta is not behaving as expected, please report here. +body: + - type: input + id: nudelta-version + attributes: + label: Version + description: What version of Nudelta are you using? + placeholder: "0.7.0" + validations: + required: true + - type: textarea + id: environment-report + attributes: + label: Operating System + description: | + Which operating system are you using and what is its version? + + * Windows - Pressing Windows+R and then type "winver" then press "Ok" + in the window that pops up. You should see something like + `Version XXXX (OS Build XXXXX.XXXX)`. Please copy that here. + * macOS - From the Apple menu, press About this Mac, and screenshot the + Window that pops up. Please remember to hide your serial number using + the screenshot editor or Preview. + * Linux - in your terminal of choice, paste `uname -a` then paste the output + here. Please also mention the distribution and version. + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: What went wrong? Be clear and concise. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/requests.yml b/.github/ISSUE_TEMPLATE/requests.yml new file mode 100644 index 0000000..13a8323 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/requests.yml @@ -0,0 +1,10 @@ +name: Feature Requests and Enhancements +description: Have any ideas? Find any shortcomings? Feel free to share them. +body: + - type: textarea + id: prompt + attributes: + label: Description + description: Is there anything currently not in Nudelta that you'd like to see added? + validations: + required: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 398ab29..7a101e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -145,6 +145,7 @@ jobs: * Please note that you will need Rosetta as there is no Apple Silicon build at the moment. On Linux, download the `.AppImage`, enable "allow executing file as program" in its properties (shown below), then double-click it. + * You may need to install `libudev` separately- on Ubuntu, you can run `sudo apt-get install -y libudev`. ![Setting Linux execute permission with the GNOME File Browser](https://raw.githubusercontent.com/donn/nudelta/main/res/linux_exec_permission.png) files: | diff --git a/.gitignore b/.gitignore index 43251e9..3b27fcf 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,7 @@ dist/ *.AppImage yarn-error.log -!OSAcknowledgements.txt \ No newline at end of file +!OSAcknowledgements.txt + +# Python +venv \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ce994a..fc43930 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,9 +13,10 @@ set(PROJECT_VERSION ${NUDELTA_VERSION}) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_OSX_ARCHITECTURES x86_64) -set(HIDAPI_WITH_LIBUSB TRUE) -set(HIDAPI_WITH_HIDRAW FALSE) +set(HIDAPI_WITH_LIBUSB FALSE) +set(HIDAPI_WITH_HIDRAW TRUE) set(BUILD_SHARED_LIBS FALSE) set(CMAKE_POSITION_INDEPENDENT_CODE ON) @@ -44,7 +45,7 @@ file(GLOB nudelta_lib_src "lib/*.cpp") add_library(nd ${nudelta_lib_src} ${CMAKE_CURRENT_BINARY_DIR}/res.cpp) add_dependencies(nd res_file) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - target_link_libraries(nd PRIVATE hidapi::libusb) + target_link_libraries(nd PRIVATE hidapi::hidraw) else () target_link_libraries(nd PRIVATE hidapi) endif () @@ -65,7 +66,7 @@ if (NODE_RUNTIME) include_directories(${CMAKE_SOURCE_DIR}/node_modules/node-api-headers/include) add_library(node-libnd SHARED src/node.cpp ${CMAKE_JS_SRC}) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - target_link_libraries(node-libnd PRIVATE hidapi::libusb) + target_link_libraries(node-libnd PRIVATE hidapi::hidraw) else () target_link_libraries(node-libnd PRIVATE hidapi) endif () @@ -83,7 +84,7 @@ add_executable(nudelta src/main.cpp) add_compile_definitions(NUDELTA_VERSION="${CMAKE_PROJECT_VERSION}") target_link_libraries(nudelta PRIVATE nd) if (CMAKE_SYSTEM_NAME STREQUAL "Linux") - target_link_libraries(nudelta PRIVATE hidapi::libusb) + target_link_libraries(nudelta PRIVATE hidapi::hidraw) else () target_link_libraries(nudelta PRIVATE hidapi) endif () diff --git a/Changelog.md b/Changelog.md index c930d69..b696ae5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +# 0.7.0 +- Updated to fix a bug for macOS 13.3 or higher +- Linux builds now use `hidraw` instead of `libusb` so the detection mechanism + remains identical for macOS and Linux + - libhidapi gives access to `usage` and `usage_page` only when using `hidraw` + on Linux # 0.6.7 - Add a no-verify CLI flag so unsupported keyboards can still dump data. - Reorder the keycodes for consistency diff --git a/Dockerfile b/Dockerfile index 23274ed..b4082ec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ RUN n 14 RUN npm i -g yarn ## LibUSB -RUN yum install -y libusbx-devel +RUN yum install -y libudev-devel ## WORKDIR /nudelta diff --git a/Readme.md b/Readme.md index 0f956eb..d979c86 100644 --- a/Readme.md +++ b/Readme.md @@ -5,7 +5,8 @@

-> Note: This software is in beta. Nudelta is an unofficial product and is not affiliated with NuPhy Studio. +> Note: This software is in beta. NuPhy® is a registered trademark of NuPhy Studio. +> Nudelta is an unofficial product and is not affiliated with NuPhy Studio. @@ -21,8 +22,7 @@ An open-source alternative to the [NuPhy Console](https://nuphy.com/pages/nuphy-console) created by reverse-engineering the keyboards' USB protocol. What this has: -* Support for NuPhy Air75 on Windows 10+, macOS 11+, and Linux - * ALPHA support for the NuPhy Halo75 +* Support for NuPhy Air75 and Halo75 on Windows 10+, macOS 11+, and Linux * Loading and saving keymap modifications from a `.yml` configuration file * The ability to back up and dump keymaps to binary formats (CLI) * The ability to dump keymaps to a human-readable hex format (CLI) @@ -103,5 +103,3 @@ nudelta -r ## License The GNU General Public License v3 or, at your option, any later version. Check '[License](/License)'. - -> NuPhy® is a registered trademark of NuPhy Studio. diff --git a/lib/nuphy.cpp b/lib/nuphy.cpp index aa0c2fe..2e212d8 100644 --- a/lib/nuphy.cpp +++ b/lib/nuphy.cpp @@ -21,6 +21,7 @@ #include #include +#include #include NuPhy::Handles NuPhy::getHandles() { @@ -148,8 +149,36 @@ static std::shared_ptr< NuPhy > createKeyboard( return nullptr; } +std::string represent_hid_struct(hid_device_info *info) { + std::stringstream str; + + str << std::hex; + + str << "At path: " << info->path << std::endl; + str << "VID/PID: " << info->vendor_id << ":" << info->product_id + << std::endl; + if (info->serial_number != nullptr) { + str << "SN: " << info->serial_number << std::endl; + } + str << "Release: " << info->release_number << std::endl; + if (info->manufacturer_string != nullptr) { + str << "Manufacturer String: " << to_utf8(info->manufacturer_string) + << std::endl; + } + if (info->product_string != nullptr) { + str << "Product String: " << to_utf8(info->product_string) << std::endl; + } + str << "Usage/Page: " << info->usage << "/" << info->usage_page + << std::endl; + str << "Interface Number: " << std::dec << info->interface_number + << std::endl; + str << "---" << std::endl; + return str.str(); +} + #ifdef _WIN32 -// Win is different: there are multiple "channels" each with a different path +// Win is different: there are multiple "channels" each with a different +// path // - So far, I've identified col06 as the one for keymap data, col05 for // requests std::string writeCol = "col05"; @@ -168,7 +197,7 @@ std::shared_ptr< NuPhy > NuPhy::find(bool verify) { std::optional< std::string > requestPath; while (seeker != nullptr) { - if (seeker->interface_number == 1 && seeker->usage == 1 + if (seeker->interface_number != -1 && seeker->usage == 1 && seeker->usage_page == 0xFF00 && (!productName.has_value() || productName.value() == to_utf8(seeker->product_string))) { @@ -233,7 +262,8 @@ std::shared_ptr< NuPhy > NuPhy::find(bool verify) { bool multipleWarned = false; std::string productString = ""; while (seeker != nullptr) { - if (seeker->interface_number == 1) { + if (seeker->interface_number != -1 && seeker->usage == 1 + && seeker->usage_page == 0xFF00) { if (keyboard != nullptr) { // We only care if the path is different, because that means a // different device on Mac and Linux diff --git a/package.json b/package.json index 6991015..4c72ade 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nudelta", "author": "Mohamed Gaber ", - "version": "0.6.7", + "version": "0.7.0", "license": "GPL-3.0-or-later", "homepage": "https://github.com/donn/nudelta#readme", "description": "An open-source alternative to the NuPhy Console", @@ -23,7 +23,7 @@ "@rollup/plugin-node-resolve": "^9.0.0", "@rollup/plugin-url": "^8.0.1", "@rollup/plugin-yaml": "^4.0.1", - "cmake-js": "^7.1.1", + "cmake-js": "^7.2.1", "electron": "^21.2.1", "electron-builder": "^23.6.0", "fast-glob": "^3.2.12", @@ -52,12 +52,6 @@ "linux": { "target": [ "appimage" - ], - "extraFiles": [ - { - "from": "/lib64/libusb-1.0.so.0", - "to": "usr/lib/libusb-1.0.so.0" - } ] }, "mac": { @@ -76,4 +70,4 @@ "tabWidth": 4, "quoteProps": "consistent" } -} +} \ No newline at end of file diff --git a/shell.nix b/shell.nix index 0be354e..868b57d 100644 --- a/shell.nix +++ b/shell.nix @@ -2,14 +2,13 @@ pkgs ? import {} }: -with pkgs; (mkShell.override { stdenv = clangStdenv; }) { +with pkgs; (mkShell.override { stdenv = if stdenv.isDarwin then darwin.apple_sdk_11_0.stdenv else clangStdenv; }) { packages = [ nodejs yarn cmake gnumake - libusb pkg-config electron - ]; + ] ++ (if stdenv.isDarwin then [darwin.apple_sdk_11_0.frameworks.IOKit darwin.apple_sdk_11_0.frameworks.AppKit] else [udev]); } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 1ed142d..6519221 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,12 @@ #include #include #include +#ifdef _MSC_VER + #include +typedef SSIZE_T ssize_t; +#else + #include +#endif #define MAX_STR 255 #ifndef NUDELTA_VERSION @@ -78,7 +84,7 @@ SSCO_Fn(resetKeymap) { SSCO_Fn(dumpKeymap) { auto mac = opts.options.find("mac") != opts.options.end(); auto verify = opts.options.find("no-verify") == opts.options.end(); - + auto keyboard = getKeyboard(verify); auto keys = keyboard->getKeymap(mac); auto file = opts.options.find("dump-keys")->second; @@ -143,7 +149,10 @@ SSCO_Fn(loadKeymap) { }; uint8_t readBuffer[1024]; - fread(readBuffer, 1, sizeof readBuffer, filePtr); + ssize_t readResult = fread(readBuffer, 1, sizeof readBuffer, filePtr); + if (readResult < 0) { + throw std::runtime_error("Failed to read the keymap file."); + } // ALERT: Endianness-defined Behavior auto keymap = std::vector< uint32_t >( diff --git a/yarn.lock b/yarn.lock index 858d84d..d3a8063 100644 --- a/yarn.lock +++ b/yarn.lock @@ -460,13 +460,14 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -axios@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" - integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== +axios@^1.3.2: + version "1.3.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" + integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ== dependencies: - follow-redirects "^1.14.9" + follow-redirects "^1.15.0" form-data "^4.0.0" + proxy-from-env "^1.1.0" balanced-match@^1.0.0: version "1.0.2" @@ -720,17 +721,17 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" -cmake-js@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/cmake-js/-/cmake-js-7.1.1.tgz#7935210efe0d562bbb02a7fde445d147e9eb4523" - integrity sha512-2NK9diAsNC0eIeKUUi+eN8uxCJukZ0TlMNZ8f1Z2PEW3Oeh0Tmn0VizPayFVnhrvZQcBK8tPqzrL4oHeaQBymw== +cmake-js@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/cmake-js/-/cmake-js-7.2.1.tgz#757c0d39994121b084bab96290baf115ee7712cd" + integrity sha512-AdPSz9cSIJWdKvm0aJgVu3X8i0U3mNTswJkSHzZISqmYVjZk7Td4oDFg0mCBA383wO+9pG5Ix7pEP1CZH9x2BA== dependencies: - axios "^0.27.2" + axios "^1.3.2" debug "^4" fs-extra "^10.1.0" lodash.isplainobject "^4.0.6" memory-stream "^1.0.0" - node-api-headers "^0.0.1" + node-api-headers "^0.0.2" npmlog "^6.0.2" rc "^1.2.7" semver "^7.3.8" @@ -1199,7 +1200,7 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -follow-redirects@^1.14.9: +follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== @@ -1938,10 +1939,10 @@ node-addon-api@^5.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.0.0.tgz#7d7e6f9ef89043befdb20c1989c905ebde18c501" integrity sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA== -node-api-headers@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/node-api-headers/-/node-api-headers-0.0.1.tgz#e49821048943e50d5b93bc12ada0dd072d2b0769" - integrity sha512-eRxckUSXMRQRV69h+ksfleQzR3BdRwkJuc/Y65KFFwhibC5G1y6wgytYW2WWTB/oG1bt+pf2RwjZDYC0xKqgqQ== +node-api-headers@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/node-api-headers/-/node-api-headers-0.0.2.tgz#31f4c6c2750b63e598128e76a60aefca6d76ac5d" + integrity sha512-YsjmaKGPDkmhoNKIpkChtCsPVaRE0a274IdERKnuc/E8K1UJdBZ4/mvI006OijlQZHCfpRNOH3dfHQs92se8gg== nodemon@^2.0.9: version "2.0.9" @@ -2135,6 +2136,11 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + pstree.remy@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a"