Blender V5.0
GHOST_NDOFManagerUnix.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6#include "GHOST_System.hh"
7
8/* Logging, use `ghost.ndof.unix.*` prefix. */
9#include "CLG_log.h"
10
11#include <cstdio>
12#include <spnav.h>
13#include <unistd.h>
14
15static const char *spnav_sock_path = "/var/run/spnav.sock";
16
17static CLG_LogRef LOG = {"ghost.ndof"};
18
20 : GHOST_NDOFManager(sys), available_(false)
21{
22 if (access(spnav_sock_path, F_OK) != 0) {
23 CLOG_DEBUG(&LOG, "'spacenavd' not found at \"%s\"", spnav_sock_path);
24 }
25 else if (spnav_open() != -1) {
26 CLOG_DEBUG(&LOG, "'spacenavd' found at\"%s\"", spnav_sock_path);
27 available_ = true;
28
29 /* determine exactly which device (if any) is plugged in */
30
31#define MAX_LINE_LENGTH 100
32
33 /* look for USB devices with Logitech or 3Dconnexion's vendor ID */
34 FILE *command_output = popen("lsusb | grep '046d:\\|256f:'", "r");
35 if (command_output) {
36 char line[MAX_LINE_LENGTH] = {0};
37 while (fgets(line, MAX_LINE_LENGTH, command_output)) {
38 ushort vendor_id = 0, product_id = 0;
39 if (sscanf(line, "Bus %*d Device %*d: ID %hx:%hx", &vendor_id, &product_id) == 2) {
40 if (setDevice(vendor_id, product_id)) {
41 break; /* stop looking once the first 3D mouse is found */
42 }
43 }
44 }
45 pclose(command_output);
46 }
47 }
48}
49
51{
52 if (available_) {
53 spnav_close();
54 }
55}
56
58{
59 return available_;
60}
61
70#define USE_FINISH_GLITCH_WORKAROUND
71/* TODO: make this available on all platforms */
72
73#ifdef USE_FINISH_GLITCH_WORKAROUND
74static bool motion_test_prev = false;
76/* NOTE(ideasman42): It's important not to generate zero-motion event immediately
77 * because the SPNAV API may not have had a chance to generate events since the last
78 * call to this function.
79 * In my tests values between 50-100ms work well, opt for the larger value
80 * to avoid false positives:
81 * - Releasing the device can cause subtle wobble generating start/finish events.
82 * - Subtle motion can *sometimes* cause start/finish events too.
83 *
84 * Furthermore, waiting a 10/th of a second for the "finish" event is fast enough
85 * that any motion occurring in a shorter time-span can be considered "continuous". */
86# define MOTION_TEST_IDLE_MS 100
87#endif /* USE_FINISH_GLITCH_WORKAROUND */
88
90{
91 bool anyProcessed = false;
92
93 if (available_) {
94 spnav_event e;
95
96#ifdef USE_FINISH_GLITCH_WORKAROUND
97 bool motion_test = false;
98#endif
99
100 while (spnav_poll_event(&e)) {
101 switch (e.type) {
102 case SPNAV_EVENT_MOTION: {
103 /* convert to blender view coords */
104 uint64_t now = system_.getMilliSeconds();
105 const int t[3] = {int(e.motion.x), int(e.motion.y), int(-e.motion.z)};
106 const int r[3] = {int(-e.motion.rx), int(-e.motion.ry), int(e.motion.rz)};
107
108 updateTranslation(t, now);
109 updateRotation(r, now);
110#ifdef USE_FINISH_GLITCH_WORKAROUND
111 motion_test = true;
113#endif
114 break;
115 }
116 case SPNAV_EVENT_BUTTON:
117 uint64_t now = system_.getMilliSeconds();
118 updateButtonRAW(e.button.bnum, e.button.press, now);
119 break;
120 }
121 anyProcessed = true;
122 }
123
124#ifdef USE_FINISH_GLITCH_WORKAROUND
125 if (motion_test_prev == true && motion_test == false) {
126 const uint64_t now = system_.getMilliSeconds();
127 GHOST_ASSERT(motion_test_prev_time <= now, "Invalid time offset");
129 /* Re-run this check next time `processEvents` is called. */
130 motion_test = true;
131 }
132 else {
133 const int v[3] = {0, 0, 0};
134
135 updateTranslation(v, now);
136 updateRotation(v, now);
137
138 anyProcessed = true;
139 }
140 }
141 motion_test_prev = motion_test;
142#endif /* USE_FINISH_GLITCH_WORKAROUND */
143 }
144
145 return anyProcessed;
146}
147
148#undef USE_FINISH_GLITCH_WORKAROUND
unsigned short ushort
#define CLOG_DEBUG(clg_ref,...)
Definition CLG_log.h:191
#define GHOST_ASSERT(x, info)
static uint64_t motion_test_prev_time
static bool motion_test_prev
#define MOTION_TEST_IDLE_MS
static const char * spnav_sock_path
#define MAX_LINE_LENGTH
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
unsigned long long int uint64_t
GHOST_NDOFManagerUnix(GHOST_System &)
void updateTranslation(const int t[3], uint64_t time)
GHOST_NDOFManager(GHOST_System &)
void updateButtonRAW(int button_number, bool press, uint64_t time)
void updateRotation(const int r[3], uint64_t time)
bool setDevice(unsigned short vendor_id, unsigned short product_id)
#define LOG(level)
Definition log.h:97