Fix #33877: Pressure Sensitivity stop after opening User Preferences Window

Issue was caused by opening the same device id twice and closing one of
descriptors on closing window. This used to close device used by other
windows.

Now moved all device-specific code to SystemX11, so opening and closing
happens only once.
This commit is contained in:
Sergey Sharybin 2013-01-16 12:28:25 +00:00
parent fe83dc6882
commit 2095b2cddb
4 changed files with 262 additions and 237 deletions

@ -146,6 +146,12 @@ GHOST_SystemX11(
XkbSetDetectableAutoRepeat(m_display, true, NULL);
}
#ifdef WITH_X11_XINPUT
/* initialize incase X11 fails to load */
memset(&m_xtablet, 0, sizeof(m_xtablet));
initXInputDevices();
#endif
}
GHOST_SystemX11::
@ -157,6 +163,15 @@ GHOST_SystemX11::
}
#endif
#ifdef WITH_X11_XINPUT
/* close tablet devices */
if (m_xtablet.StylusDevice)
XCloseDevice(m_display, m_xtablet.StylusDevice);
if (m_xtablet.EraserDevice)
XCloseDevice(m_display, m_xtablet.EraserDevice);
#endif /* WITH_X11_XINPUT */
XCloseDisplay(m_display);
}
@ -494,12 +509,12 @@ processEvents(
#ifdef WITH_X11_XINPUT
/* set currently using tablet mode (stylus or eraser) depending on device ID */
static void setTabletMode(GHOST_WindowX11 *window, XID deviceid)
static void setTabletMode(GHOST_SystemX11 *system, GHOST_WindowX11 *window, XID deviceid)
{
if (deviceid == window->GetXTablet().StylusID)
window->GetXTablet().CommonData.Active = GHOST_kTabletModeStylus;
else if (deviceid == window->GetXTablet().EraserID)
window->GetXTablet().CommonData.Active = GHOST_kTabletModeEraser;
if (deviceid == system->GetXTablet().StylusID)
window->GetTabletData()->Active = GHOST_kTabletModeStylus;
else if (deviceid == system->GetXTablet().EraserID)
window->GetTabletData()->Active = GHOST_kTabletModeEraser;
}
#endif /* WITH_X11_XINPUT */
@ -537,7 +552,7 @@ GHOST_SystemX11::processEvent(XEvent *xe)
XMotionEvent &xme = xe->xmotion;
#ifdef WITH_X11_XINPUT
bool is_tablet = window->GetXTablet().CommonData.Active != GHOST_kTabletModeNone;
bool is_tablet = window->GetTabletData()->Active != GHOST_kTabletModeNone;
#else
bool is_tablet = false;
#endif
@ -988,31 +1003,31 @@ GHOST_SystemX11::processEvent(XEvent *xe)
default: {
#ifdef WITH_X11_XINPUT
if (xe->type == window->GetXTablet().MotionEvent) {
if (xe->type == m_xtablet.MotionEvent) {
XDeviceMotionEvent *data = (XDeviceMotionEvent *)xe;
/* stroke might begin without leading ProxyIn event,
* this happens when window is opened when stylus is already hovering
* around tablet surface */
setTabletMode(window, data->deviceid);
setTabletMode(this, window, data->deviceid);
window->GetXTablet().CommonData.Pressure =
data->axis_data[2] / ((float)window->GetXTablet().PressureLevels);
window->GetTabletData()->Pressure =
data->axis_data[2] / ((float)m_xtablet.PressureLevels);
/* the (short) cast and the &0xffff is bizarre and unexplained anywhere,
* but I got garbage data without it. Found it in the xidump.c source --matt */
window->GetXTablet().CommonData.Xtilt =
(short)(data->axis_data[3] & 0xffff) / ((float)window->GetXTablet().XtiltLevels);
window->GetXTablet().CommonData.Ytilt =
(short)(data->axis_data[4] & 0xffff) / ((float)window->GetXTablet().YtiltLevels);
window->GetTabletData()->Xtilt =
(short)(data->axis_data[3] & 0xffff) / ((float)m_xtablet.XtiltLevels);
window->GetTabletData()->Ytilt =
(short)(data->axis_data[4] & 0xffff) / ((float)m_xtablet.YtiltLevels);
}
else if (xe->type == window->GetXTablet().ProxInEvent) {
else if (xe->type == m_xtablet.ProxInEvent) {
XProximityNotifyEvent *data = (XProximityNotifyEvent *)xe;
setTabletMode(window, data->deviceid);
setTabletMode(this, window, data->deviceid);
}
else if (xe->type == window->GetXTablet().ProxOutEvent) {
window->GetXTablet().CommonData.Active = GHOST_kTabletModeNone;
else if (xe->type == m_xtablet.ProxOutEvent) {
window->GetTabletData()->Active = GHOST_kTabletModeNone;
}
#endif // WITH_X11_XINPUT
break;
@ -1653,3 +1668,185 @@ GHOST_TSuccess GHOST_SystemX11::pushDragDropEvent(GHOST_TEventType eventType,
);
}
#endif
#ifdef WITH_X11_XINPUT
/*
* Dummy function to get around IO Handler exiting if device invalid
* Basically it will not crash blender now if you have a X device that
* is configured but not plugged in.
*/
static int ApplicationErrorHandler(Display *display, XErrorEvent *theEvent)
{
fprintf(stderr, "Ignoring Xlib error: error code %d request code %d\n",
theEvent->error_code, theEvent->request_code);
/* No exit! - but keep lint happy */
return 0;
}
/* These C functions are copied from Wine 1.1.13's wintab.c */
#define BOOL int
#define TRUE 1
#define FALSE 0
static bool match_token(const char *haystack, const char *needle)
{
const char *p, *q;
for (p = haystack; *p; )
{
while (*p && isspace(*p))
p++;
if (!*p)
break;
for (q = needle; *q && *p && tolower(*p) == tolower(*q); q++)
p++;
if (!*q && (isspace(*p) || !*p))
return TRUE;
while (*p && !isspace(*p))
p++;
}
return FALSE;
}
/* Determining if an X device is a Tablet style device is an imperfect science.
* We rely on common conventions around device names as well as the type reported
* by Wacom tablets. This code will likely need to be expanded for alternate tablet types
*
* Wintab refers to any device that interacts with the tablet as a cursor,
* (stylus, eraser, tablet mouse, airbrush, etc)
* this is not to be confused with wacom x11 configuration "cursor" device.
* Wacoms x11 config "cursor" refers to its device slot (which we mirror with
* our gSysCursors) for puck like devices (tablet mice essentially).
*/
#if 0 // unused
static BOOL is_tablet_cursor(const char *name, const char *type)
{
int i;
static const char *tablet_cursor_whitelist[] = {
"wacom",
"wizardpen",
"acecad",
"tablet",
"cursor",
"stylus",
"eraser",
"pad",
NULL
};
for (i = 0; tablet_cursor_whitelist[i] != NULL; i++) {
if (name && match_token(name, tablet_cursor_whitelist[i]))
return TRUE;
if (type && match_token(type, tablet_cursor_whitelist[i]))
return TRUE;
}
return FALSE;
}
#endif
static BOOL is_stylus(const char *name, const char *type)
{
int i;
static const char *tablet_stylus_whitelist[] = {
"stylus",
"wizardpen",
"acecad",
NULL
};
for (i = 0; tablet_stylus_whitelist[i] != NULL; i++) {
if (name && match_token(name, tablet_stylus_whitelist[i]))
return TRUE;
if (type && match_token(type, tablet_stylus_whitelist[i]))
return TRUE;
}
return FALSE;
}
static BOOL is_eraser(const char *name, const char *type)
{
if (name && match_token(name, "eraser"))
return TRUE;
if (type && match_token(type, "eraser"))
return TRUE;
return FALSE;
}
#undef BOOL
#undef TRUE
#undef FALSE
/* end code copied from wine */
void GHOST_SystemX11::initXInputDevices()
{
static XErrorHandler old_handler = (XErrorHandler) 0;
XExtensionVersion *version = XGetExtensionVersion(m_display, INAME);
if (version && (version != (XExtensionVersion *)NoSuchExtension)) {
if (version->present) {
int device_count;
XDeviceInfo *device_info = XListInputDevices(m_display, &device_count);
m_xtablet.StylusDevice = NULL;
m_xtablet.EraserDevice = NULL;
/* Install our error handler to override Xlib's termination behavior */
old_handler = XSetErrorHandler(ApplicationErrorHandler);
for (int i = 0; i < device_count; ++i) {
char *device_type = device_info[i].type ? XGetAtomName(m_display, device_info[i].type) : NULL;
// printf("Tablet type:'%s', name:'%s', index:%d\n", device_type, device_info[i].name, i);
if (m_xtablet.StylusDevice == NULL && is_stylus(device_info[i].name, device_type)) {
// printf("\tfound stylus\n");
m_xtablet.StylusID = device_info[i].id;
m_xtablet.StylusDevice = XOpenDevice(m_display, m_xtablet.StylusID);
if (m_xtablet.StylusDevice != NULL) {
/* Find how many pressure levels tablet has */
XAnyClassPtr ici = device_info[i].inputclassinfo;
for (int j = 0; j < m_xtablet.StylusDevice->num_classes; ++j) {
if (ici->c_class == ValuatorClass) {
// printf("\t\tfound ValuatorClass\n");
XValuatorInfo *xvi = (XValuatorInfo *)ici;
m_xtablet.PressureLevels = xvi->axes[2].max_value;
/* this is assuming that the tablet has the same tilt resolution in both
* positive and negative directions. It would be rather weird if it didn't.. */
m_xtablet.XtiltLevels = xvi->axes[3].max_value;
m_xtablet.YtiltLevels = xvi->axes[4].max_value;
break;
}
ici = (XAnyClassPtr)(((char *)ici) + ici->length);
}
}
else {
m_xtablet.StylusID = 0;
}
}
else if (m_xtablet.EraserDevice == NULL && is_eraser(device_info[i].name, device_type)) {
// printf("\tfound eraser\n");
m_xtablet.EraserID = device_info[i].id;
m_xtablet.EraserDevice = XOpenDevice(m_display, m_xtablet.EraserID);
if (m_xtablet.EraserDevice == NULL) m_xtablet.EraserID = 0;
}
if (device_type) {
XFree((void *)device_type);
}
}
/* Restore handler */
(void) XSetErrorHandler(old_handler);
XFreeDeviceList(device_info);
}
XFree(version);
}
}
#endif /* WITH_X11_XINPUT */

@ -39,6 +39,11 @@
#include "GHOST_System.h"
#include "../GHOST_Types.h"
// For tablets
#ifdef WITH_X11_XINPUT
# include <X11/extensions/XInput.h>
#endif
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
# define GHOST_X11_RES_NAME "Blender" /* res_name */
# define GHOST_X11_RES_CLASS "Blender" /* res_class */
@ -284,6 +289,27 @@ public:
Atom m_incr;
Atom m_utf8_string;
#ifdef WITH_X11_XINPUT
typedef struct GHOST_TabletX11 {
XDevice *StylusDevice;
XDevice *EraserDevice;
XID StylusID, EraserID;
int MotionEvent;
int ProxInEvent;
int ProxOutEvent;
int PressureLevels;
int XtiltLevels, YtiltLevels;
} GHOST_TabletX11;
GHOST_TabletX11 &GetXTablet()
{
return m_xtablet;
}
#endif // WITH_X11_XINPUT
private:
Display *m_display;
@ -291,6 +317,11 @@ private:
XIM m_xim;
#endif
#ifdef WITH_X11_XINPUT
/* Tablet devices */
GHOST_TabletX11 m_xtablet;
#endif
/// The vector of windows that need to be updated.
std::vector<GHOST_WindowX11 *> m_dirty_windows;
@ -313,6 +344,10 @@ private:
bool openX11_IM();
#endif
#ifdef WITH_X11_XINPUT
void initXInputDevices();
#endif
GHOST_WindowX11 *
findGhostWindow(
Window xwind

@ -186,11 +186,6 @@ GHOST_WindowX11(
int natom;
int glxVersionMajor, glxVersionMinor; /* As in GLX major.minor */
#ifdef WITH_X11_XINPUT
/* initialize incase X11 fails to load */
memset(&m_xtablet, 0, sizeof(m_xtablet));
#endif
m_visual = NULL;
if (!glXQueryVersion(m_display, &glxVersionMajor, &glxVersionMinor)) {
@ -517,200 +512,30 @@ bool GHOST_WindowX11::createX11_XIC()
#endif
#ifdef WITH_X11_XINPUT
/*
* Dummy function to get around IO Handler exiting if device invalid
* Basically it will not crash blender now if you have a X device that
* is configured but not plugged in.
*/
static int ApplicationErrorHandler(Display *display, XErrorEvent *theEvent)
{
fprintf(stderr, "Ignoring Xlib error: error code %d request code %d\n",
theEvent->error_code, theEvent->request_code);
/* No exit! - but keep lint happy */
return 0;
}
/* These C functions are copied from Wine 1.1.13's wintab.c */
#define BOOL int
#define TRUE 1
#define FALSE 0
static bool match_token(const char *haystack, const char *needle)
{
const char *p, *q;
for (p = haystack; *p; )
{
while (*p && isspace(*p))
p++;
if (!*p)
break;
for (q = needle; *q && *p && tolower(*p) == tolower(*q); q++)
p++;
if (!*q && (isspace(*p) || !*p))
return TRUE;
while (*p && !isspace(*p))
p++;
}
return FALSE;
}
/* Determining if an X device is a Tablet style device is an imperfect science.
* We rely on common conventions around device names as well as the type reported
* by Wacom tablets. This code will likely need to be expanded for alternate tablet types
*
* Wintab refers to any device that interacts with the tablet as a cursor,
* (stylus, eraser, tablet mouse, airbrush, etc)
* this is not to be confused with wacom x11 configuration "cursor" device.
* Wacoms x11 config "cursor" refers to its device slot (which we mirror with
* our gSysCursors) for puck like devices (tablet mice essentially).
*/
#if 0 // unused
static BOOL is_tablet_cursor(const char *name, const char *type)
{
int i;
static const char *tablet_cursor_whitelist[] = {
"wacom",
"wizardpen",
"acecad",
"tablet",
"cursor",
"stylus",
"eraser",
"pad",
NULL
};
for (i = 0; tablet_cursor_whitelist[i] != NULL; i++) {
if (name && match_token(name, tablet_cursor_whitelist[i]))
return TRUE;
if (type && match_token(type, tablet_cursor_whitelist[i]))
return TRUE;
}
return FALSE;
}
#endif
static BOOL is_stylus(const char *name, const char *type)
{
int i;
static const char *tablet_stylus_whitelist[] = {
"stylus",
"wizardpen",
"acecad",
NULL
};
for (i = 0; tablet_stylus_whitelist[i] != NULL; i++) {
if (name && match_token(name, tablet_stylus_whitelist[i]))
return TRUE;
if (type && match_token(type, tablet_stylus_whitelist[i]))
return TRUE;
}
return FALSE;
}
static BOOL is_eraser(const char *name, const char *type)
{
if (name && match_token(name, "eraser"))
return TRUE;
if (type && match_token(type, "eraser"))
return TRUE;
return FALSE;
}
#undef BOOL
#undef TRUE
#undef FALSE
/* end code copied from wine */
void GHOST_WindowX11::initXInputDevices()
{
static XErrorHandler old_handler = (XErrorHandler) 0;
XExtensionVersion *version = XGetExtensionVersion(m_display, INAME);
if (version && (version != (XExtensionVersion *)NoSuchExtension)) {
if (version->present) {
int device_count;
XDeviceInfo *device_info = XListInputDevices(m_display, &device_count);
m_xtablet.StylusDevice = NULL;
m_xtablet.EraserDevice = NULL;
m_xtablet.CommonData.Active = GHOST_kTabletModeNone;
/* Install our error handler to override Xlib's termination behavior */
old_handler = XSetErrorHandler(ApplicationErrorHandler);
for (int i = 0; i < device_count; ++i) {
char *device_type = device_info[i].type ? XGetAtomName(m_display, device_info[i].type) : NULL;
// printf("Tablet type:'%s', name:'%s', index:%d\n", device_type, device_info[i].name, i);
if (m_xtablet.StylusDevice == NULL && is_stylus(device_info[i].name, device_type)) {
// printf("\tfound stylus\n");
m_xtablet.StylusID = device_info[i].id;
m_xtablet.StylusDevice = XOpenDevice(m_display, m_xtablet.StylusID);
if (m_xtablet.StylusDevice != NULL) {
/* Find how many pressure levels tablet has */
XAnyClassPtr ici = device_info[i].inputclassinfo;
for (int j = 0; j < m_xtablet.StylusDevice->num_classes; ++j) {
if (ici->c_class == ValuatorClass) {
// printf("\t\tfound ValuatorClass\n");
XValuatorInfo *xvi = (XValuatorInfo *)ici;
m_xtablet.PressureLevels = xvi->axes[2].max_value;
/* this is assuming that the tablet has the same tilt resolution in both
* positive and negative directions. It would be rather weird if it didn't.. */
m_xtablet.XtiltLevels = xvi->axes[3].max_value;
m_xtablet.YtiltLevels = xvi->axes[4].max_value;
break;
}
ici = (XAnyClassPtr)(((char *)ici) + ici->length);
}
}
else {
m_xtablet.StylusID = 0;
}
}
else if (m_xtablet.EraserDevice == NULL && is_eraser(device_info[i].name, device_type)) {
// printf("\tfound eraser\n");
m_xtablet.EraserID = device_info[i].id;
m_xtablet.EraserDevice = XOpenDevice(m_display, m_xtablet.EraserID);
if (m_xtablet.EraserDevice == NULL) m_xtablet.EraserID = 0;
}
if (device_type) {
XFree((void *)device_type);
}
}
/* Restore handler */
(void) XSetErrorHandler(old_handler);
XFreeDeviceList(device_info);
GHOST_SystemX11::GHOST_TabletX11 &xtablet = m_system->GetXTablet();
XEventClass xevents[10], ev;
int dcount = 0;
if (m_xtablet.StylusDevice) {
DeviceMotionNotify(m_xtablet.StylusDevice, m_xtablet.MotionEvent, ev);
if (xtablet.StylusDevice) {
DeviceMotionNotify(xtablet.StylusDevice, xtablet.MotionEvent, ev);
if (ev) xevents[dcount++] = ev;
ProximityIn(m_xtablet.StylusDevice, m_xtablet.ProxInEvent, ev);
ProximityIn(xtablet.StylusDevice, xtablet.ProxInEvent, ev);
if (ev) xevents[dcount++] = ev;
ProximityOut(m_xtablet.StylusDevice, m_xtablet.ProxOutEvent, ev);
ProximityOut(xtablet.StylusDevice, xtablet.ProxOutEvent, ev);
if (ev) xevents[dcount++] = ev;
}
if (m_xtablet.EraserDevice) {
DeviceMotionNotify(m_xtablet.EraserDevice, m_xtablet.MotionEvent, ev);
if (xtablet.EraserDevice) {
DeviceMotionNotify(xtablet.EraserDevice, xtablet.MotionEvent, ev);
if (ev) xevents[dcount++] = ev;
ProximityIn(m_xtablet.EraserDevice, m_xtablet.ProxInEvent, ev);
ProximityIn(xtablet.EraserDevice, xtablet.ProxInEvent, ev);
if (ev) xevents[dcount++] = ev;
ProximityOut(m_xtablet.EraserDevice, m_xtablet.ProxOutEvent, ev);
ProximityOut(xtablet.EraserDevice, xtablet.ProxOutEvent, ev);
if (ev) xevents[dcount++] = ev;
}
@ -1341,15 +1166,6 @@ GHOST_WindowX11::
XFreeCursor(m_display, m_custom_cursor);
}
#ifdef WITH_X11_XINPUT
/* close tablet devices */
if (m_xtablet.StylusDevice)
XCloseDevice(m_display, m_xtablet.StylusDevice);
if (m_xtablet.EraserDevice)
XCloseDevice(m_display, m_xtablet.EraserDevice);
#endif /* WITH_X11_XINPUT */
if (m_context != s_firstContext) {
glXDestroyContext(m_display, m_context);
}

@ -196,31 +196,9 @@ public:
getXWindow(
);
#ifdef WITH_X11_XINPUT
typedef struct GHOST_TabletX11
GHOST_TabletData *GetTabletData()
{
GHOST_TabletData CommonData;
XDevice *StylusDevice;
XDevice *EraserDevice;
XID StylusID, EraserID;
int MotionEvent;
int ProxInEvent;
int ProxOutEvent;
int PressureLevels;
int XtiltLevels, YtiltLevels;
} GHOST_TabletX11;
GHOST_TabletX11 &GetXTablet()
{
return m_xtablet;
}
const GHOST_TabletData *GetTabletData()
{
return &m_xtablet.CommonData;
return &m_tabletData;
}
#else // WITH_X11_XINPUT
const GHOST_TabletData *GetTabletData()
@ -386,8 +364,7 @@ private:
#endif
#ifdef WITH_X11_XINPUT
/* Tablet devices */
GHOST_TabletX11 m_xtablet;
GHOST_TabletData m_tabletData;
#endif
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)