Skip to content

Commit 1d1dd3b

Browse files
committed
Revert "Revert back to hidapi module after finding fix for macOS exclusive mode"
This reverts commit 8a86159.
1 parent ebfe721 commit 1d1dd3b

File tree

13 files changed

+393
-44
lines changed

13 files changed

+393
-44
lines changed

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ include test/*.py
3333
include test/gui_qt/*.py
3434
include tox.ini
3535
include windows/*
36+
exclude dev/*
3637
exclude .pre-commit-config.yaml
3738
# without first including it, exluding .readthedocs.yml results in a warning when running locally
3839
include .readthedocs.yml

dev/build_hidapi.sh

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Build hidapi library locally for development.
5+
#
6+
# Usage:
7+
# ./dev/macos_dev_setup.sh
8+
# MACOS_UNIVERSAL2=0 ./dev/build_hidapi.sh # macOS: don't build universal2
9+
#
10+
# Resulting libs:
11+
# macOS: build/local-hidapi/macos/lib/libhidapi*.dylib
12+
# Linux: build/local-hidapi/linux/lib/libhidapi-hidraw.so
13+
# Windows: build/local-hidapi/windows/bin/hidapi.dll
14+
15+
. ./plover_build_utils/deps.sh
16+
17+
MACOS_UNIVERSAL2="${MACOS_UNIVERSAL2:-1}"
18+
19+
# Resolve repo root relative to this script
20+
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
21+
REPO_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
22+
23+
WORK_DIR="${REPO_ROOT}/build/local-hidapi"
24+
SRC_DIR="${WORK_DIR}/src"
25+
BUILD_DIR="${WORK_DIR}/build"
26+
OUT_LIB_DIR_MAC="${WORK_DIR}/macos/lib"
27+
OUT_LIB_DIR_LNX="${WORK_DIR}/linux/lib"
28+
OUT_BIN_DIR_WIN="${WORK_DIR}/windows/bin"
29+
30+
# Clean and prep
31+
# Prepare directories
32+
mkdir -p "${SRC_DIR}" "${BUILD_DIR}"
33+
34+
OS="$(uname -s || true)"
35+
# If outputs already exist for the current platform, skip rebuilding.
36+
if [[ "${OS}" == "Darwin" ]]; then
37+
if ls "${OUT_LIB_DIR_MAC}"/libhidapi*.dylib >/dev/null 2>&1; then
38+
echo "hidapi found at ${OUT_LIB_DIR_MAC}; skipping build."
39+
exit 0
40+
fi
41+
elif [[ "${OS}" == "Linux" ]]; then
42+
if ls "${OUT_LIB_DIR_LNX}"/libhidapi-hidraw.so* >/dev/null 2>&1; then
43+
echo "hidapi found at ${OUT_LIB_DIR_LNX}; skipping build."
44+
exit 0
45+
fi
46+
elif [[ "${OS}" == MINGW* || "${OS}" == MSYS* || "${OS}" == CYGWIN* || "${OS}" == "Windows_NT" ]]; then
47+
if [ -f "${OUT_BIN_DIR_WIN}/hidapi.dll" ]; then
48+
echo "hidapi found at ${OUT_BIN_DIR_WIN}/hidapi.dll; skipping build."
49+
exit 0
50+
fi
51+
fi
52+
53+
# Clean and prep for a fresh build
54+
rm -rf "${SRC_DIR}" "${BUILD_DIR}"
55+
mkdir -p "${SRC_DIR}" "${BUILD_DIR}"
56+
57+
TARBALL="${WORK_DIR}/hidapi-${HIDAPI_VERSION}.tar.gz"
58+
59+
60+
if [[ "${OS}" == "Darwin" ]]; then
61+
echo "==> Configuring (macOS, IOHIDManager backend)"
62+
63+
# Shared fetch/build helpers (macOS)
64+
# shellcheck disable=SC1091
65+
. "${REPO_ROOT}/osx/build_hidapi.sh"
66+
67+
echo "==> Downloading & unpacking hidapi ${HIDAPI_VERSION}"
68+
fetch_hidapi_macos "${HIDAPI_VERSION}" "${SRC_DIR}" "${TARBALL}"
69+
70+
mkdir -p "${OUT_LIB_DIR_MAC}"
71+
# Architectures
72+
if [[ "${MACOS_UNIVERSAL2}" == "1" ]]; then
73+
OSX_ARCHES="x86_64;arm64"
74+
else
75+
ARCH="$(uname -m || true)"
76+
if [[ "${ARCH}" == "arm64" || "${ARCH}" == "aarch64" ]]; then
77+
OSX_ARCHES="arm64"
78+
else
79+
OSX_ARCHES="x86_64"
80+
fi
81+
fi
82+
83+
cmake_build_macos "${SRC_DIR}" "${BUILD_DIR}" "${OSX_ARCHES}" "RelWithDebInfo"
84+
85+
# Find produced dylib
86+
DYLIB="$(/usr/bin/find "${BUILD_DIR}" -type f -name 'libhidapi*.dylib' -print -quit || true)"
87+
if [[ -z "${DYLIB}" ]]; then
88+
echo "Error: libhidapi*.dylib not found in build output." >&2
89+
exit 3
90+
fi
91+
92+
BASENAME="$(basename "${DYLIB}")"
93+
echo "==> Staging ${BASENAME}"
94+
cp -f "${DYLIB}" "${OUT_LIB_DIR_MAC}/${BASENAME}"
95+
96+
pushd "${OUT_LIB_DIR_MAC}" >/dev/null
97+
ln -sf "${BASENAME}" libhidapi.dylib || true
98+
popd >/dev/null
99+
100+
echo
101+
echo "✅ Built macOS hidapi at: ${OUT_LIB_DIR_MAC}/${BASENAME}"
102+
103+
elif [[ "${OS}" == "Linux" ]]; then
104+
echo "==> Configuring (Linux, hidraw backend)"
105+
mkdir -p "${OUT_LIB_DIR_LNX}"
106+
107+
# Shared fetch/build helpers (Linux)
108+
# shellcheck disable=SC1091
109+
. "${REPO_ROOT}/linux/build_hidapi.sh"
110+
111+
echo "==> Downloading & unpacking hidapi ${HIDAPI_VERSION}"
112+
fetch_hidapi_linux "${HIDAPI_VERSION}" "${SRC_DIR}" "${TARBALL}"
113+
cmake_build_linux "${SRC_DIR}" "${BUILD_DIR}" "RelWithDebInfo"
114+
115+
SO="$(/usr/bin/find "${BUILD_DIR}" -type f -name 'libhidapi-hidraw.so*' -print -quit || true)"
116+
if [[ -z "${SO}" ]]; then
117+
echo "Error: libhidapi-hidraw.so not found in build output." >&2
118+
exit 3
119+
fi
120+
121+
BASENAME="$(basename "${SO}")"
122+
echo "==> Staging ${BASENAME}"
123+
cp -f "${SO}" "${OUT_LIB_DIR_LNX}/${BASENAME}"
124+
125+
pushd "${OUT_LIB_DIR_LNX}" >/dev/null
126+
[[ -e libhidapi-hidraw.so ]] || ln -sf "${BASENAME}" libhidapi-hidraw.so || true
127+
popd >/dev/null
128+
129+
echo
130+
echo "✅ Built Linux hidapi at: ${OUT_LIB_DIR_LNX}/${BASENAME}"
131+
132+
elif [[ "${OS}" == MINGW* || "${OS}" == MSYS* || "${OS}" == CYGWIN* || "${OS}" == "Windows_NT" ]]; then
133+
echo "==> Configuring (Windows, WinAPI backend)"
134+
mkdir -p "${OUT_BIN_DIR_WIN}"
135+
136+
# Shared fetch/build helpers (Windows)
137+
# shellcheck disable=SC1091
138+
. "${REPO_ROOT}/windows/build_hidapi.sh"
139+
140+
echo "==> Downloading & unpacking hidapi ${HIDAPI_VERSION}"
141+
fetch_hidapi_windows "${HIDAPI_VERSION}" "${SRC_DIR}" "${TARBALL}"
142+
143+
# Build shared DLL
144+
cmake_build_windows "${SRC_DIR}" "${BUILD_DIR}" "Release"
145+
146+
# Find the produced DLL (varies by generator)
147+
DLL="$(/usr/bin/find "${BUILD_DIR}" -type f \( -iname 'hidapi*.dll' -o -iname 'libhidapi*.dll' \) -print -quit 2>/dev/null || true)"
148+
if [[ -z "${DLL}" ]]; then
149+
echo "Error: hidapi DLL not found in build output." >&2
150+
exit 3
151+
fi
152+
153+
BASENAME="$(basename "${DLL}")"
154+
echo "==> Staging ${BASENAME}"
155+
cp -f "${DLL}" "${OUT_BIN_DIR_WIN}/hidapi.dll"
156+
157+
echo
158+
echo "✅ Built Windows hidapi at: ${OUT_BIN_DIR_WIN}/hidapi.dll"
159+
else
160+
echo "Unsupported OS: ${OS}. This helper currently supports macOS, Linux, and Windows." >&2
161+
exit 4
162+
fi

dev/write_hidapi_pth.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Write a .pth into site-packages that adds the local hidapi bin dir
4+
to the DLL search path on Windows via os.add_dll_directory.
5+
6+
Usage (optional): write_hidapi_pth.py <repo_dir> <env_sitepackages_dir>
7+
"""
8+
9+
from __future__ import annotations
10+
import sys
11+
from pathlib import Path
12+
13+
14+
def main(argv: list[str]) -> int:
15+
if sys.platform != "win32":
16+
# No-op outside Windows.
17+
return 0
18+
19+
if len(argv) >= 3:
20+
repo_dir = Path(argv[1]).resolve()
21+
site_packages = Path(argv[2]).resolve()
22+
else:
23+
repo_dir = Path.cwd()
24+
site_packages = Path(sys.prefix, "Lib", "site-packages")
25+
26+
dll_dir = repo_dir / "build" / "local-hidapi" / "windows" / "bin"
27+
if not dll_dir.is_dir():
28+
print(f"[hidapi .pth] skip: {dll_dir} does not exist", file=sys.stderr)
29+
return 0
30+
31+
site_packages.mkdir(parents=True, exist_ok=True)
32+
pth_path = site_packages / "plover_hidapi_add_dll_dir.pth"
33+
34+
# Keep it one line: .pth executes arbitrary Python on import.
35+
code = (
36+
"import os,sys;"
37+
"p=r%r;"
38+
"getattr(os,'add_dll_directory',lambda *_:None)(p) if sys.platform=='win32' else None\n"
39+
% (str(dll_dir),)
40+
)
41+
42+
try:
43+
pth_path.write_text(code, encoding="utf-8")
44+
print(f"[hidapi .pth] wrote {pth_path}")
45+
return 0
46+
except Exception as e:
47+
print(f"[hidapi .pth] failed to write {pth_path}: {e}", file=sys.stderr)
48+
return 2
49+
50+
51+
if __name__ == "__main__":
52+
sys.exit(main(sys.argv))

linux/appimage/build.sh

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
set -e
44

5+
. ./plover_build_utils/deps.sh
56
. ./plover_build_utils/functions.sh
67

78
topdir="$PWD"
@@ -196,6 +197,34 @@ run "$linuxdeploy" \
196197
# Install Plover and dependencies.
197198
bootstrap_dist "$wheel"
198199

200+
# ------- Start: Build & bundle hidapi from source -------
201+
hidapi_src="$builddir/hidapi-src"
202+
hidapi_bld="$builddir/hidapi-build"
203+
hidapi_tar="$builddir/hidapi.tar.gz"
204+
205+
. ./linux/build_hidapi.sh
206+
207+
echo "Downloading and unpacking hidapi ${HIDAPI_VERSION}"
208+
fetch_hidapi_linux "$HIDAPI_VERSION" "$hidapi_src" "$hidapi_tar"
209+
210+
cmake_build_linux "$hidapi_src" "$hidapi_bld" "Release"
211+
212+
# Locate the produced .so
213+
hidapi_so="$(find "$hidapi_bld" -name 'libhidapi-hidraw.so*' -type f -print -quit)"
214+
if [ -z "$hidapi_so" ] || [ ! -f "$hidapi_so" ]; then
215+
echo "Error: built libhidapi-hidraw.so not found." >&2
216+
exit 3
217+
fi
218+
219+
# Bundle into the AppDir's lib directory
220+
run cp -v "$hidapi_so" "$appdir/usr/lib/"
221+
base="$(basename "$hidapi_so")"
222+
# Add symlink for unversioned .so if needed
223+
if [ ! -e "$appdir/usr/lib/libhidapi-hidraw.so" ]; then
224+
ln -s "$base" "$appdir/usr/lib/libhidapi-hidraw.so"
225+
fi
226+
# ------- End: Build & bundle hidapi from source -------
227+
199228
# Trim the fat, second pass.
200229
run "$python" -m plover_build_utils.trim "$appdir" "$builddir/blacklist.txt"
201230

linux/build_hidapi.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
need() { command -v "$1" >/dev/null 2>&1 || { echo "Error: '$1' not found" >&2; exit 2; }; }
2+
3+
fetch_hidapi_linux() {
4+
local version="$1" src_dir="$2" tarball="$3"
5+
local url="https://github.com/libusb/hidapi/archive/refs/tags/hidapi-${version}.tar.gz"
6+
need curl; need tar
7+
rm -rf "$src_dir"
8+
mkdir -p "$src_dir"
9+
curl -fsSL "$url" -o "$tarball"
10+
tar -xzf "$tarball" -C "$src_dir" --strip-components=1
11+
}
12+
13+
cmake_build_linux() {
14+
local src="$1" bld="$2" config="${3:-Release}"
15+
need cmake
16+
rm -rf "$bld"
17+
cmake -S "$src" -B "$bld" \
18+
-DBUILD_SHARED_LIBS=ON \
19+
-DCMAKE_BUILD_TYPE="$config"
20+
cmake --build "$bld" --config "$config" --parallel
21+
}

osx/build_hidapi.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
need() { command -v "$1" >/dev/null 2>&1 || { echo "Error: '$1' not found" >&2; exit 2; }; }
2+
3+
fetch_hidapi_macos() {
4+
local version="$1" src_dir="$2" tarball="$3"
5+
local url="https://github.com/libusb/hidapi/archive/refs/tags/hidapi-${version}.tar.gz"
6+
need curl; need tar
7+
rm -rf "$src_dir"
8+
mkdir -p "$src_dir"
9+
curl -fsSL "$url" -o "$tarball"
10+
tar -xzf "$tarball" -C "$src_dir" --strip-components=1
11+
}
12+
13+
cmake_build_macos() {
14+
local src="$1" bld="$2" arches="$3" config="${4:-Release}"
15+
need cmake
16+
rm -rf "$bld"
17+
cmake -S "$src" -B "$bld" \
18+
-DBUILD_SHARED_LIBS=ON \
19+
-DCMAKE_BUILD_TYPE="$config" \
20+
-DCMAKE_OSX_ARCHITECTURES="$arches"
21+
cmake --build "$bld" --config "$config" --parallel
22+
}

osx/make_app.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
set -e
44

5+
. ./plover_build_utils/deps.sh
56
. ./plover_build_utils/functions.sh
67

78
topdir="$PWD"
@@ -65,6 +66,37 @@ python='appdir_python'
6566
# Install Plover and dependencies.
6667
bootstrap_dist "$plover_wheel"
6768

69+
# ------- Start: Build & bundle hidapi from source -------
70+
. ./osx/build_hidapi.sh
71+
72+
hidapi_src="$builddir/hidapi-src"
73+
hidapi_bld="$builddir/hidapi-build"
74+
hidapi_tar="$builddir/hidapi.tar.gz"
75+
76+
echo "Downloading and unpacking hidapi ${HIDAPI_VERSION}"
77+
fetch_hidapi_macos "$HIDAPI_VERSION" "$hidapi_src" "$hidapi_tar"
78+
79+
cmake_build_macos "$hidapi_src" "$hidapi_bld" "x86_64;arm64" "Release"
80+
81+
# Locate the produced dylib
82+
hidapi_dylib="$(/usr/bin/find "$hidapi_bld" -name 'libhidapi*.dylib' -type f -print -quit)"
83+
if [ -z "$hidapi_dylib" ] || [ ! -f "$hidapi_dylib" ]; then
84+
echo "Error: built libhidapi*.dylib not found." >&2
85+
exit 3
86+
fi
87+
88+
# Bundle into the app's Frameworks directory
89+
run cp "$hidapi_dylib" "$frameworks_dir/"
90+
base="$(basename "$hidapi_dylib")"
91+
92+
# Add alias to Frameworks dir
93+
ln -sfn "$base" "$frameworks_dir/libhidapi.dylib"
94+
95+
# Add RPATH to the Python binary itself and re-sign it
96+
run install_name_tool -add_rpath "@executable_path/../../../../../Frameworks" "$py_binary"
97+
run /usr/bin/codesign -f -s - "$py_binary"
98+
# ------- End: Build & bundle hidapi from source -------
99+
68100
# Create launcher.
69101
run gcc -Wall -O2 -arch x86_64 -arch arm64 'osx/app_resources/plover_launcher.c' -o "$macos_dir/Plover"
70102

0 commit comments

Comments
 (0)