Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,22 @@ cmake_minimum_required(VERSION 3.1...3.19)
project(
libspnavdev
VERSION 0.1
LANGUAGES C)
)

if(WIN32)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()

if(EMSCRIPTEN)
set (CMAKE_CXX_STANDARD 17)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
SET_SOURCE_FILES_PROPERTIES(
src/serdev.c
src/spnavdev.c
src/usbdev.c
PROPERTIES LANGUAGE CXX )
endif()

add_library(spnavdev
src/serdev.c
src/spnavdev.c
Expand All @@ -42,12 +52,15 @@ else()
)
endif()

if(WIN32 OR APPLE OR EMSCRIPTEN)
if(WIN32 OR APPLE)
set(HAVE_HIDAPI TRUE)
set(BUILD_SHARED_LIBS FALSE)
add_subdirectory(extlib/hidapi)
target_link_libraries(spnavdev PRIVATE hidapi::hidapi)
target_include_directories(spnavdev PRIVATE ${CMAKE_BINARY_DIR}/extlib/hidapi/hidapi)
elseif(EMSCRIPTEN)
set(HAVE_WEBHIDAPI TRUE)
set(BUILD_SHARED_LIBS FALSE)
else()
find_package(hidapi)
if(hidapi_FOUND)
Expand All @@ -66,6 +79,8 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
# We are our own project, not a subproject
if(WIN32)
add_subdirectory(examples/win32)
elseif(EMSCRIPTEN)
add_subdirectory(examples/web)
else()
add_subdirectory(examples/unix)
endif()
Expand Down
49 changes: 44 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ It is easily portable to embedded systems, robot controllers, non-UNIX systems e
The goal of spnavdev is to handle all USB and serial 6DOF devices, across multiple platforms such
as: all UNIX systems, windows, DOS, and possibly even bare metal on certain systems.

The library also works in [web browsers](#building-for-web) that support the [WebHID][webhid] api.

[webhid]: https://caniuse.com/webhid

### Supported devices

The library supports all USB devices from 3Dconnexion and should support all serial devices as
well. The following devices are tested and fully working - 6DOF input, buttons, LEDs (if present):

#### USB devices (all by 3Dconnexion):
#### Tested USB devices (all by 3Dconnexion):

Device | VID | PID | Note
------ | --- | --- | ----
Expand All @@ -29,7 +33,7 @@ SpaceExplorer | 0x046d | 0xc627 |

"New" devices (with VID = 0x256f) should work but I have not personally tested them.

#### Serial devices:
#### Tested Serial devices:

Device | Manufacturer | Note
------ | ------------ | ----
Expand Down Expand Up @@ -71,7 +75,7 @@ sudo dnf install git gcc-c++ cmake

Before building, [check out the project and the necessary submodules](#Building).

After that, build libspnavdev as following:
After that, build `libspnavdev` as following:

```sh
mkdir build
Expand Down Expand Up @@ -122,7 +126,7 @@ Open `build\libspnavdev.sln` with Visual C++ and build it.

#### Building with MinGW

It is also possible to build libspnavdev using [MinGW][mingw].
It is also possible to build `libspnavdev` using [MinGW][mingw].

First, ensure that git and gcc are in your `$PATH`. Then, run the following in bash:

Expand All @@ -137,10 +141,45 @@ make
[cmakewin]: http://www.cmake.org/download/#latest
[mingw]: http://www.mingw.org/

### Building for web

You will need the usual build tools, cmake and [Emscripten][]. On a Debian derivative (e.g. Ubuntu) dependencies other than Emscripten can be installed with:

```sh
apt-get install git build-essential cmake
```

First, install and prepare `emsdk`:

```sh
git clone https://github.com/emscripten-core/emsdk
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
cd ..
```

Before building, [check out the project and the necessary submodules](#Building).

After that, build `libspnavdev` as following:

```sh
mkdir build
cd build
emcmake cmake .. -DCMAKE_BUILD_TYPE=Release
make
```

The example application is built as three files in the `build/examples/web` directory with names
starting with `test`. It can be run locally with `emrun build/examples/web/test.html`.

[emscripten]: https://emscripten.org/

History
-------
John Tsiombikas (the author of [spacenavd](https://github.com/FreeSpacenav/spacenavd)) [started writing this library](https://github.com/FreeSpacenav/libspnavdev)
in November 2020 with the inntention to:
in November 2020 with the intention to:

> eventually replace the
> device handling code in spacenavd v2, and will also be usable as a standalone
Expand Down
1 change: 1 addition & 0 deletions config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef SPNAVDEV_CONFIG_H_
#define SPNAVDEV_CONFIG_H_
#cmakedefine HAVE_HIDAPI
#cmakedefine HAVE_WEBHIDAPI
#endif
36 changes: 36 additions & 0 deletions examples/web/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# libspnavdev - direct 6dof device handling library
# Copyright (C) 2021 Collabora, Ltd.
#
# SPDX-License-Identifier: GPL-3-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

add_executable(test test.c)
target_link_libraries(test PRIVATE spnavdev)

if(EMSCRIPTEN)
set(M_LIBRARY "" CACHE STRING "libm (not necessary)" FORCE)

set_target_properties(test PROPERTIES LINK_FLAGS
"--shell-file=${CMAKE_CURRENT_SOURCE_DIR}/test.html
--bind
-s ALLOW_MEMORY_GROWTH=1 -s WASM=1 -s ASYNCIFY=1
-s DYNCALLS=1 -s ASSERTIONS=1
-s EXPORTED_RUNTIME_METHODS=ccall,cwrap
-s EXPORTED_FUNCTIONS=_test,_main
-s TOTAL_STACK=33554432 -s TOTAL_MEMORY=134217728"
SUFFIX ".html")
set_property(TARGET test APPEND PROPERTY LINK_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/test.html")
endif()
64 changes: 64 additions & 0 deletions examples/web/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include "spnavdev.h"

static struct spndev *dev;
static int quit = 0;

void usercallback(union spndev_event ev, void* uptr) {
static int led = 0;
const char* s;
struct spndev* dev = (struct spndev*)uptr;

switch (ev.type) {
case SPNDEV_MOTION:
printf("motion: T[%+6d %+6d %+6d] R[%+6d %+6d %+6d]\n",
ev.mot.v[0], ev.mot.v[1], ev.mot.v[2], ev.mot.v[3],
ev.mot.v[4], ev.mot.v[5]);
break;

case SPNDEV_BUTTON:
if ((s = spndev_button_name(dev, ev.bn.num))) {
printf("button %d (\"%s\") ", ev.bn.num, s);
}
else {
printf("button %d ", ev.bn.num);
}
puts(ev.bn.press ? "pressed" : "released");

if (ev.bn.press) {
spndev_set_led(dev, led);
spndev_write_lcd(dev, 0xAA);
spndev_set_lcd_bl(dev, led);
led = !led;
}

break;

default:
break;
}
}

extern int test()
{
if(!(dev = spndev_open(0))) {
fprintf(stderr, "Failed to open 6dof device\n");
return 1;
}

spndev_set_userptr(dev, (void*)dev);
spndev_set_usercallback(dev, usercallback);

printf("Monitoring device, ctrl-c to quit\n");

return 0;
}

int main(int argc, char** argv) {
printf("Press the button to test\n");
return 0;
}
46 changes: 46 additions & 0 deletions examples/web/test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>libspnavdev WebHID test</title>
<style>
body { font: 16px/1.5 sans-serif; margin: 2rem auto; max-width: 95%; }
button { padding: 0.6rem 1rem; }
textarea { box-sizing: border-box; display: block; height: 25rem; margin-top: 1rem; width: 100%; background-color:#000; color:#fff; font-family:'Lucida Console',Monaco,monospace; }
</style>
<script>
var Module = {
print: function () {
var message = Array.prototype.join.call(arguments, " ");
console.log(message);
appendOutput(message);
},
printErr: function (message) {
console.error(message);
appendOutput(message);
},
onRuntimeInitialized: function () {
document.getElementById("request-device").disabled = false;
}
};

function appendOutput(message) {
var output = document.getElementById("output");
output.value += message + "\n";
output.scrollTop = output.scrollHeight;
}

function requestHIDDevice() {
Module.ccall("test", null, [], [], { async: true });
}
</script>
</head>
<body>
<h1>libspnavdev WebHID test</h1>
<p>Connect a supported SpaceMouse, press the button below, then grant access to it in the browser prompt.</p>
<button id="request-device" onclick="requestHIDDevice()" disabled>Request HID device</button>
<textarea id="output" rows="12" readonly></textarea>
{{{ SCRIPT }}}
</body>
</html>
3 changes: 3 additions & 0 deletions src/dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,13 @@ struct spndev {
int lcdbl;

void *uptr, *drvdata;
spndev_callback *callback;

void (*close)(struct spndev*);
int (*read)(struct spndev*, union spndev_event*);

int (*setcallback)(struct spndev*, spndev_callback callback);

void (*setled)(struct spndev*, int led);
int (*getled)(struct spndev*);

Expand Down
3 changes: 2 additions & 1 deletion src/serdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ static struct {
{"Magellan SpaceMouse", 11, {"1", "2", "3", "4", "5", "6", "7", "8", "*", "+", "-"}},
{"Spaceball 5000FLX", 12, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C"}},
{"CadMan", 4, {"1", "2", "3", "4"}},
{"Space Explorer", 14, {"1", "2", "T", "L", "R", "F", "ALT", "ESC", "SHIFT", "CTRL", "Fit", "Panel", "+", "-", "2D"}},
{"Space Explorer", 15, {"1", "2", "T", "L", "R", "F", "ALT", "ESC", "SHIFT", "CTRL", "Fit", "Panel", "+", "-", "2D"}}, // 3DX-700027
{0, 0, {0}}
};

Expand Down Expand Up @@ -102,6 +102,7 @@ int spndev_ser_open(struct spndev *dev, const char *devstr)
dev->drvdata = sb;
dev->close = close_dev_serial;
dev->read = read_dev_serial;
dev->setcallback = 0;
dev->setled = 0;
dev->getled = 0;
dev->setlcdbl = 0;
Expand Down
10 changes: 10 additions & 0 deletions src/spnavdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "spnavdev.h"
#include "dev.h"

Expand All @@ -29,6 +30,7 @@ struct spndev *spndev_open(const char *devstr)
perror("spndev_open: failed to allocate device structure");
return 0;
}
memset(dev, 0, sizeof *dev);

if(!devstr || sscanf(devstr, "%hx:%hx", &vendor, &product) == 2) {
if(spndev_usb_open(dev, devstr, vendor, product) == -1) {
Expand Down Expand Up @@ -62,6 +64,14 @@ void *spndev_get_userptr(struct spndev *dev)
return dev->uptr;
}

int spndev_set_usercallback(struct spndev* dev, spndev_callback callback) {
dev->callback = callback;
if (!dev->setcallback) {
return -1;
}
return dev->setcallback(dev, callback);
}

/* device information */
const char *spndev_name(struct spndev *dev)
{
Expand Down
4 changes: 4 additions & 0 deletions src/spnavdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ union spndev_event {
struct spndev_event_button bn;
};

typedef void (spndev_callback)(union spndev_event ev, void *uptr);

/* possible devstr parameters:
* - null: auto-detect (USB only)
* - device file (e.g. COM6 or /dev/ttyS0 or /dev/input/event42)
Expand All @@ -60,6 +62,8 @@ void spndev_close(struct spndev *dev);
void spndev_set_userptr(struct spndev *dev, void *uptr);
void *spndev_get_userptr(struct spndev *dev);

int spndev_set_usercallback(struct spndev* dev, spndev_callback callback);

/* device information */
const char *spndev_name(struct spndev *dev);
const char *spndev_path(struct spndev *dev);
Expand Down
Loading
Loading