Merge branch 'master' into blender2.8
[blender.git] / intern / ghost / intern / GHOST_NDOFManagerUnix.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s):
19  *   Mike Erwin
20  *
21  * ***** END GPL LICENSE BLOCK *****
22  */
23
24 #include "GHOST_NDOFManagerUnix.h"
25 #include "GHOST_System.h"
26
27 #include <spnav.h>
28 #include <stdio.h>
29 #include <unistd.h>
30
31 #define SPNAV_SOCK_PATH "/var/run/spnav.sock"
32
33 GHOST_NDOFManagerUnix::GHOST_NDOFManagerUnix(GHOST_System& sys)
34     : GHOST_NDOFManager(sys),
35       m_available(false)
36 {
37         if (access(SPNAV_SOCK_PATH, F_OK) != 0) {
38 #ifdef DEBUG
39                 /* annoying for official builds, just adds noise and most people don't own these */
40                 puts("ndof: spacenavd not found");
41                 /* This isn't a hard error, just means the user doesn't have a 3D mouse. */
42 #endif
43         }
44         else if (spnav_open() != -1) {
45                 m_available = true;
46
47                 /* determine exactly which device (if any) is plugged in */
48
49 #define MAX_LINE_LENGTH 100
50
51                 /* look for USB devices with Logitech or 3Dconnexion's vendor ID */
52                 FILE *command_output = popen("lsusb | grep '046d:\\|256f:'", "r");
53                 if (command_output) {
54                         char line[MAX_LINE_LENGTH] = {0};
55                         while (fgets(line, MAX_LINE_LENGTH, command_output)) {
56                                 unsigned short vendor_id = 0, product_id = 0;
57                                 if (sscanf(line, "Bus %*d Device %*d: ID %hx:%hx", &vendor_id, &product_id) == 2)
58                                         if (setDevice(vendor_id, product_id)) {
59                                                 break; /* stop looking once the first 3D mouse is found */
60                                         }
61                         }
62                         pclose(command_output);
63                 }
64         }
65 }
66
67 GHOST_NDOFManagerUnix::~GHOST_NDOFManagerUnix()
68 {
69         if (m_available)
70                 spnav_close();
71 }
72
73 bool GHOST_NDOFManagerUnix::available()
74 {
75         return m_available;
76 }
77
78 /*
79  * Workaround for a problem where we don't enter the 'GHOST_kFinished' state,
80  * this causes any proceeding event to have a very high 'dt' (time delta),
81  * many seconds for eg, causing the view to jump.
82  *
83  * this workaround expects continuous events, if we miss a motion event,
84  * immediately send a dummy event with no motion to ensure the finished state is reached.
85  */
86 #define USE_FINISH_GLITCH_WORKAROUND
87 /* TODO: make this available on all platforms */
88
89 #ifdef USE_FINISH_GLITCH_WORKAROUND
90 static bool motion_test_prev = false;
91 #endif
92
93 bool GHOST_NDOFManagerUnix::processEvents()
94 {
95         bool anyProcessed = false;
96
97         if (m_available) {
98                 spnav_event e;
99
100 #ifdef USE_FINISH_GLITCH_WORKAROUND
101                 bool motion_test = false;
102 #endif
103
104                 while (spnav_poll_event(&e)) {
105                         switch (e.type) {
106                                 case SPNAV_EVENT_MOTION:
107                                 {
108                                         /* convert to blender view coords */
109                                         GHOST_TUns64 now = m_system.getMilliSeconds();
110                                         const int t[3] = {(int)e.motion.x, (int)e.motion.y, (int)-e.motion.z};
111                                         const int r[3] = {(int)-e.motion.rx, (int)-e.motion.ry, (int)e.motion.rz};
112
113                                         updateTranslation(t, now);
114                                         updateRotation(r, now);
115 #ifdef USE_FINISH_GLITCH_WORKAROUND
116                                         motion_test = true;
117 #endif
118                                         break;
119                                 }
120                                 case SPNAV_EVENT_BUTTON:
121                                         GHOST_TUns64 now = m_system.getMilliSeconds();
122                                         updateButton(e.button.bnum, e.button.press, now);
123                                         break;
124                         }
125                         anyProcessed = true;
126                 }
127
128 #ifdef USE_FINISH_GLITCH_WORKAROUND
129                 if (motion_test_prev == true && motion_test == false) {
130                         GHOST_TUns64 now = m_system.getMilliSeconds();
131                         const int v[3] = {0, 0, 0};
132
133                         updateTranslation(v, now);
134                         updateRotation(v, now);
135
136                         anyProcessed = true;
137                 }
138                 motion_test_prev = motion_test;
139 #endif
140
141         }
142
143         return anyProcessed;
144 }