X11 independant implementation for USB SpaceNavigator, works with Wayland

This commit is contained in:
Torsten Sadowski
2018-10-17 23:11:13 +02:00
committed by wmayer
parent 7cff64643e
commit b7fbbd97be
5 changed files with 85 additions and 290 deletions

View File

@@ -0,0 +1,49 @@
/*
Implementation by Torsten Sadowski 2018
*/
#include "GuiApplicationNativeEventAware.h"
#include "SpaceballEvent.h"
#include <QWidget>
#if defined(SPNAV_FOUND)
#include <spnav.h>
#endif
void Gui::GUIApplicationNativeEventAware::pollSpacenav()
{
spnav_event ev;
while(spnav_poll_event(&ev))
{
QWidget *currentWidget = this->focusWidget();
if (!currentWidget)
return;
if (!setOSIndependentMotionData()) return;
importSettings();
switch (ev.type)
{
case SPNAV_EVENT_MOTION:
{
Spaceball::MotionEvent *motionEvent = new Spaceball::MotionEvent();
motionEvent->setTranslations(ev.motion.x, ev.motion.y, ev.motion.z);
motionEvent->setRotations(ev.motion.rx, ev.motion.ry, ev.motion.rz);
this->postEvent(currentWidget, motionEvent);
break;
}
case SPNAV_EVENT_BUTTON:
{
Spaceball::ButtonEvent *buttonEvent = new Spaceball::ButtonEvent();
buttonEvent->setButtonNumber(ev.button.bnum);
if (ev.button.press)
{
buttonEvent->setButtonStatus(Spaceball::BUTTON_PRESSED);
}
else
{
buttonEvent->setButtonStatus(Spaceball::BUTTON_RELEASED);
}
break;
}
}
}
}

View File

@@ -106,12 +106,6 @@ else()
endif()
IF(SPNAV_FOUND)
if (BUILD_QT5 AND UNIX AND NOT APPLE)
find_package(Qt5X11Extras REQUIRED)
include_directories(${Qt5X11Extras_INCLUDE_DIRS})
list(APPEND FreeCADGui_LIBS ${Qt5X11Extras_LIBRARIES})
endif()
add_definitions(-DSPNAV_FOUND)
include_directories(
${SPNAV_INCLUDE_DIR}
@@ -119,12 +113,6 @@ IF(SPNAV_FOUND)
list(APPEND FreeCADGui_LIBS
${SPNAV_LIBRARIES}
)
find_package(X11 QUIET)
if (X11_FOUND)
list(APPEND FreeCADGui_LIBS
${X11_X11_LIB}
)
endif(X11_FOUND)
ENDIF(SPNAV_FOUND)
IF(OCULUS_FOUND)
@@ -208,6 +196,13 @@ SET(FreeCADGui_SDK_SRCS
SOURCE_GROUP("3D connexion SDK" FILES ${FreeCADGui_SDK_SRCS})
endif(FREECAD_USE_3DCONNEXION AND APPLE)
if(UNIX AND NOT APPLE)
SET(FreeCADGui_SDK_SRCS
3Dconnexion/GuiApplicationNativeEventAwareLinux.cpp
)
SOURCE_GROUP("3D connexion SDK" FILES ${FreeCADGui_SDK_SRCS})
endif(UNIX AND NOT APPLE)
set(Gui_MOC_HDRS
Action.h
ActionFunction.h

View File

@@ -31,11 +31,10 @@
#include "SpaceballEvent.h"
#include "Application.h"
#if defined(Q_OS_LINUX) && defined(SPNAV_FOUND)
#include <QX11Info>
#include <spnav.h>
#if QT_VERSION >= 0x050000
#include <X11/Xlib.h>
#include <QTimer>
#undef Bool
#undef CursorShape
#undef Expose
@@ -98,23 +97,16 @@ void Gui::GUIApplicationNativeEventAware::initSpaceball(QMainWindow *window)
mainWindow = window;
#if defined(Q_OS_LINUX) && defined(SPNAV_FOUND)
if (spnav_x11_open(QX11Info::display(), window->winId()) == -1) {
if (spnav_open() == -1) {
Base::Console().Log("Couldn't connect to spacenav daemon\n");
} else {
Base::Console().Log("Connected to spacenav daemon\n");
spaceballPresent = true;
#if QT_VERSION >= 0x050000
static auto evFilter( [](void *msg, long *result){
Q_UNUSED(result);
auto inst(dynamic_cast<Gui::GUIApplicationNativeEventAware *>(QApplication::instance()));
if (inst) {
return inst->xcbEventFilter(static_cast<const xcb_client_message_event_t *>(msg));
} else {
return false;
}
} );
qApp->installNativeEventFilter(new Gui::RawInputEventFilter(evFilter));
QTimer* SpacenavPollTimer = new QTimer(this);
connect(SpacenavPollTimer, &QTimer::timeout, this, &GUIApplicationNativeEventAware::pollSpacenav);
SpacenavPollTimer->start(20);
#endif // #if QT_VERSION >= 0x050000
}
@@ -179,6 +171,7 @@ void Gui::GUIApplicationNativeEventAware::initSpaceball(QMainWindow *window)
bool Gui::GUIApplicationNativeEventAware::processSpaceballEvent(QObject *object, QEvent *event)
{
std::cout << "Gui::GUIApplicationNativeEventAware::processSpaceballEvent" << std::endl;
if (!activeWindow()) {
qDebug("No active window\n");
return true;
@@ -390,232 +383,4 @@ void Gui::GUIApplicationNativeEventAware::importSettings()
}
}
#if defined(Q_OS_LINUX)
#if QT_VERSION >= 0x050000
bool Gui::GUIApplicationNativeEventAware::xcbEventFilter(const xcb_client_message_event_t *xcb_ev)
{
#if defined(SPNAV_FOUND)
spnav_event navEvent;
// Qt4 used XEvents in native event filters, but Qt5 changed to XCB. The
// SpaceNavigator API only works with XEvent, so we need to construct a
// temporary XEvent with just enough information for spnav_x11_event()
if ((xcb_ev->response_type & 0x7F) == XCB_CLIENT_MESSAGE) {
XClientMessageEvent xev;
xev.type = ClientMessage;
xev.message_type = xcb_ev->type;
memcpy(xev.data.b, xcb_ev->data.data8, sizeof(xev.data.b));
xev.serial = 0; // These are just to squash warnings...
xev.send_event = 0;
xev.display = 0;
xev.window = 0;
xev.format = 0;
if (!spnav_x11_event(reinterpret_cast<XEvent *>(&xev), &navEvent)) {
return false;
}
} else {
return false;
}
// navEvent is now initialised
auto currentWidget(focusWidget());
if (!currentWidget) {
currentWidget = mainWindow;
}
switch (navEvent.type) {
case SPNAV_EVENT_MOTION:
{
motionDataArray[0] = navEvent.motion.x;
motionDataArray[1] = navEvent.motion.y;
motionDataArray[2] = navEvent.motion.z;
motionDataArray[3] = navEvent.motion.rx;
motionDataArray[4] = navEvent.motion.ry;
motionDataArray[5] = navEvent.motion.rz;
setOSIndependentMotionData();
importSettings();
auto motionEvent(new Spaceball::MotionEvent());
motionEvent->setTranslations( motionDataArray[0],
motionDataArray[1],
motionDataArray[2] );
motionEvent->setRotations( motionDataArray[3],
motionDataArray[4],
motionDataArray[5] );
postEvent(currentWidget, motionEvent);
return true;
}
case SPNAV_EVENT_BUTTON:
{
auto buttonEvent(new Spaceball::ButtonEvent());
buttonEvent->setButtonNumber(navEvent.button.bnum);
if (navEvent.button.press) {
buttonEvent->setButtonStatus(Spaceball::BUTTON_PRESSED);
} else {
buttonEvent->setButtonStatus(Spaceball::BUTTON_RELEASED);
}
postEvent(currentWidget, buttonEvent);
return true;
}
default:
Base::Console().Log("Unknown spaceball event\n");
return true;
} // end switch (navEvent.type) {
#else
Q_UNUSED(xcb_ev);
return false;
#endif // if/else defined(SPNAV_FOUND)
}
#else // if QT_VERSION >= 0x050000
bool Gui::GUIApplicationNativeEventAware::x11EventFilter(XEvent *event)
{
#ifdef SPNAV_FOUND
/*
First we check if we have a motion flush event:
- If there are unprocessed motion events we are in a flooding situation.
In that case we wait with generating a Spaceball event.
- A motion event counter of 0 indicates that FreeCAD is ready to process
the event. A Spaceball event, using the saved motion data, is posted.
*/
static Display* display = QX11Info::display();
static Atom motion_flush_event = XInternAtom(display, "FCMotionFlushEvent", false);
static int nMotionEvents = 0;
QWidget *currentWidget = this->focusWidget();
if (!currentWidget)
currentWidget = mainWindow;
if (event->type == ClientMessage)
{
Atom message_type = event->xclient.message_type;
if (message_type == motion_flush_event)
{
nMotionEvents--;
if (nMotionEvents == 0)
{
importSettings();
Spaceball::MotionEvent *motionEvent = new Spaceball::MotionEvent();
motionEvent->setTranslations(motionDataArray[0], motionDataArray[1], motionDataArray[2]);
motionEvent->setRotations(motionDataArray[3], motionDataArray[4], motionDataArray[5]);
this->postEvent(currentWidget, motionEvent);
}
return true;
} // XEvent: motion_flush_event
} // XEvent: ClientMessage
/*
From here on we deal with spacenav events only:
- motion: The event data is saved and a self addressed flush event
is sent through the window system (XEvent).
In the case of an event flooding, the motion data is added up.
- button: A Spaceball event is posted (QInputEvent).
*/
spnav_event navEvent;
if (!spnav_x11_event(event, &navEvent))
return false;
if (navEvent.type == SPNAV_EVENT_MOTION)
{
/*
If the motion data of the preceding event has not been processed
through posting an Spaceball event (flooding situation),
the motion data provided by the incoming event is added to the saved data.
*/
int dx, dy, dz, drx, dry, drz;
if (nMotionEvents == 0)
{
dx = 0;
dy = 0;
dz = 0;
drx = 0;
dry = 0;
drz = 0;
}
else
{
dx = motionDataArray[0];
dy = motionDataArray[1];
dz = motionDataArray[2];
drx = motionDataArray[3];
dry = motionDataArray[4];
drz = motionDataArray[5];
}
motionDataArray[0] = navEvent.motion.x;
motionDataArray[1] = navEvent.motion.y;
motionDataArray[2] = navEvent.motion.z;
motionDataArray[3] = navEvent.motion.rx;
motionDataArray[4] = navEvent.motion.ry;
motionDataArray[5] = navEvent.motion.rz;
if (!setOSIndependentMotionData()) return false;
motionDataArray[0] += dx;
motionDataArray[1] += dy;
motionDataArray[2] += dz;
motionDataArray[3] += drx;
motionDataArray[4] += dry;
motionDataArray[5] += drz;
/*
Send a self addressed flush event through the window system. This will
trigger a Spaceball event if FreeCAD is ready to do so.
*/
nMotionEvents++;
XClientMessageEvent flushEvent;
flushEvent.display = display;
flushEvent.window = event->xclient.window;
flushEvent.type = ClientMessage;
flushEvent.format = 8;
flushEvent.message_type = motion_flush_event;
XSendEvent (display, flushEvent.window, False, 0, (XEvent*)&flushEvent); // siehe spnavd, False, 0
return true;
}
if (navEvent.type == SPNAV_EVENT_BUTTON)
{
Spaceball::ButtonEvent *buttonEvent = new Spaceball::ButtonEvent();
buttonEvent->setButtonNumber(navEvent.button.bnum);
if (navEvent.button.press)
buttonEvent->setButtonStatus(Spaceball::BUTTON_PRESSED);
else
buttonEvent->setButtonStatus(Spaceball::BUTTON_RELEASED);
this->postEvent(currentWidget, buttonEvent);
return true;
}
Base::Console().Log("Unknown spaceball event\n");
return true;
#else
Q_UNUSED(event);
return false;
#endif // SPNAV_FOUND
}
#endif // if/else QT_VERSION >= 0x050000
#endif // Q_OS_LINUX
#include "moc_GuiApplicationNativeEventAware.cpp"

View File

@@ -26,17 +26,10 @@
#define GUIAPPLICATIONNATIVEEVENTAWARE_H
#include <QApplication>
#if QT_VERSION >= 0x050000
#include <QAbstractNativeEventFilter>
#endif
class QMainWindow;
#if defined(Q_OS_LINUX) && QT_VERSION >= 0x050000
#include <xcb/xcb.h>
#include <xcb/xproto.h>
#elif defined(Q_OS_WIN) && defined(_USE_3DCONNEXION_SDK)
#if defined(Q_OS_WIN) && defined(_USE_3DCONNEXION_SDK)
#include "3Dconnexion/MouseParameters.h"
#include <vector>
@@ -44,6 +37,7 @@ class QMainWindow;
//#define _WIN32_WINNT 0x0501 //target at least windows XP
#include <Windows.h>
#include <QAbstractNativeEventFilter>
#elif defined(Q_OS_MACX) && defined(_USE_3DCONNEXION_SDK)
@@ -63,25 +57,6 @@ extern void CleanupConnexionHandlers(void) __attribute__((weak_import));
namespace Gui
{
#if QT_VERSION >= 0x050000
class RawInputEventFilter : public QAbstractNativeEventFilter
{
public:
typedef bool (*EventFilter)(void *message, long *result);
RawInputEventFilter(EventFilter filter) : eventFilter(filter) {
}
virtual ~RawInputEventFilter() {
}
virtual bool nativeEventFilter(const QByteArray & /*eventType*/, void *message, long *result) {
return eventFilter(message, result);
}
private:
EventFilter eventFilter;
};
#endif // if QT_VERSION >= 0x050000
class GUIApplicationNativeEventAware : public QApplication
{
Q_OBJECT
@@ -99,19 +74,31 @@ namespace Gui
void importSettings();
float convertPrefToSensitivity(int value);
// For X11
#ifdef Q_OS_LINUX
public:
#if QT_VERSION >= 0x050000
bool xcbEventFilter(const xcb_client_message_event_t *message);
#else
bool x11EventFilter(XEvent *event);
#endif // if/else QT_VERSION >= 0x050000
void pollSpacenav();
#endif // Q_OS_LINUX
#ifdef _USE_3DCONNEXION_SDK
// For Windows
#ifdef Q_OS_WIN
class RawInputEventFilter : public QAbstractNativeEventFilter
{
public:
typedef bool (*EventFilter)(void *message, long *result);
RawInputEventFilter(EventFilter filter) : eventFilter(filter) {
}
virtual ~RawInputEventFilter() {
}
virtual bool nativeEventFilter(const QByteArray & /*eventType*/, void *message, long *result) {
return eventFilter(message, result);
}
private:
EventFilter eventFilter;
};
public:
static bool Is3dmouseAttached();

View File

@@ -47,7 +47,6 @@
#include "NativeEvent.h"
#ifdef HAVE_SPACENAV_LIB
#include <QX11Info>
#include <spnav.h>
#endif //HAVE_SPACENAV_LIB
@@ -91,7 +90,7 @@ SpaceNavigatorDevice::SpaceNavigatorDevice()
#ifdef HAVE_SPACENAV_LIB
PRIVATE(this)->hasdevice =
spnav_x11_open(QX11Info::display(), PRIVATE(this)->windowid) == -1 ? false : true;
spnav_open() == -1 ? false : true;
// FIXME: Use a debugmessage mechanism instead? (20101020 handegar)
if (!PRIVATE(this)->hasdevice) {
@@ -108,7 +107,7 @@ SpaceNavigatorDevice::SpaceNavigatorDevice(QuarterWidget *quarter) :
#ifdef HAVE_SPACENAV_LIB
PRIVATE(this)->hasdevice =
spnav_x11_open(QX11Info::display(), PRIVATE(this)->windowid) == -1 ? false : true;
spnav_open() == -1 ? false : true;
// FIXME: Use a debugmessage mechanism instead? (20101020 handegar)
if (!PRIVATE(this)->hasdevice) {
@@ -136,7 +135,7 @@ SpaceNavigatorDevice::translateEvent(QEvent * event)
XEvent * xev = ce->getEvent();
spnav_event spev;
if(spnav_x11_event(xev, &spev)) {
if(spnav_event(xev, &spev)) {
if(spev.type == SPNAV_EVENT_MOTION) {
// Add rotation
const float axislen = sqrt(spev.motion.rx*spev.motion.rx +