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