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"