Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brechtvanlommel@gmail.com>2018-12-27 17:22:20 +0300
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2018-12-27 18:18:37 +0300
commit53a6a0b2584a6730d6cfc03f3312ff0e5da91a4f (patch)
tree045e3188ef5627692739c338567e64eb25096f20 /intern/ghost
parent2c196de56bbb163048b08f321983234a5e72e804 (diff)
X11: support multiple tablet devices.
Previously we would try to guess what the main tablet device is, but this is error prone. Now we keep a list of X11 devices and try to match events to them. On the Blender side there are still some limitations in regards to using multiple devices at the same time, but this should improve things already. Fixes T59645.
Diffstat (limited to 'intern/ghost')
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp248
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.h19
-rw-r--r--intern/ghost/intern/GHOST_WindowX11.cpp47
3 files changed, 143 insertions, 171 deletions
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index e53379a4f22..1afffb85037 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -235,9 +235,6 @@ GHOST_SystemX11(
}
#endif /* USE_XINPUT_HOTPLUG */
- /* initialize incase X11 fails to load */
- memset(&m_xtablet, 0, sizeof(m_xtablet));
-
refreshXInputDevices();
#endif /* WITH_X11_XINPUT */
}
@@ -252,12 +249,8 @@ 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);
+ /* Close tablet devices. */
+ clearXInputDevices();
#endif /* WITH_X11_XINPUT */
if (m_xkb_descr) {
@@ -769,17 +762,6 @@ processEvents(
#ifdef WITH_X11_XINPUT
-/* set currently using tablet mode (stylus or eraser) depending on device ID */
-static void setTabletMode(GHOST_SystemX11 *system, GHOST_WindowX11 *window, XID deviceid)
-{
- 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 */
-
-#ifdef WITH_X11_XINPUT
static bool checkTabletProximity(Display *display, XDevice *device)
{
/* we could have true/false/not-found return value, but for now false is OK */
@@ -876,9 +858,15 @@ GHOST_SystemX11::processEvent(XEvent *xe)
* but for now enough parts of the code are checking 'Active'
* - campbell */
if (window->GetTabletData()->Active != GHOST_kTabletModeNone) {
- if (checkTabletProximity(xe->xany.display, m_xtablet.StylusDevice) == false &&
- checkTabletProximity(xe->xany.display, m_xtablet.EraserDevice) == false)
- {
+ bool any_proximity = false;
+
+ for (GHOST_TabletX11& xtablet: m_xtablets) {
+ if (checkTabletProximity(xe->xany.display, xtablet.Device)) {
+ any_proximity = true;
+ }
+ }
+
+ if (!any_proximity) {
// printf("proximity disable\n");
window->GetTabletData()->Active = GHOST_kTabletModeNone;
}
@@ -1472,59 +1460,64 @@ GHOST_SystemX11::processEvent(XEvent *xe)
default:
{
#ifdef WITH_X11_XINPUT
- if (xe->type == m_xtablet.MotionEvent ||
- xe->type == m_xtablet.MotionEventEraser ||
- xe->type == m_xtablet.PressEvent ||
- xe->type == m_xtablet.PressEventEraser)
- {
- XDeviceMotionEvent *data = (XDeviceMotionEvent *)xe;
- const unsigned char axis_first = data->first_axis;
- const unsigned char axes_end = axis_first + data->axes_count; /* after the last */
- int axis_value;
-
- /* stroke might begin without leading ProxyIn event,
- * this happens when window is opened when stylus is already hovering
- * around tablet surface */
- setTabletMode(this, window, data->deviceid);
-
- /* Note: This event might be generated with incomplete dataset (don't exactly know why, looks like in
- * some cases, if the value does not change, it is not included in subsequent XDeviceMotionEvent
- * events). So we have to check which values this event actually contains!
- */
+ for (GHOST_TabletX11& xtablet: m_xtablets) {
+ if (xe->type == xtablet.MotionEvent || xe->type == xtablet.PressEvent) {
+ XDeviceMotionEvent *data = (XDeviceMotionEvent *)xe;
+ if (data->deviceid != xtablet.ID) {
+ continue;
+ }
+
+ const unsigned char axis_first = data->first_axis;
+ const unsigned char axes_end = axis_first + data->axes_count; /* after the last */
+ int axis_value;
+
+ /* stroke might begin without leading ProxyIn event,
+ * this happens when window is opened when stylus is already hovering
+ * around tablet surface */
+ window->GetTabletData()->Active = xtablet.mode;
+
+ /* Note: This event might be generated with incomplete dataset (don't exactly know why, looks like in
+ * some cases, if the value does not change, it is not included in subsequent XDeviceMotionEvent
+ * events). So we have to check which values this event actually contains!
+ */
#define AXIS_VALUE_GET(axis, val) \
((axis_first <= axis && axes_end > axis) && ((void)(val = data->axis_data[axis - axis_first]), true))
- if (AXIS_VALUE_GET(2, axis_value)) {
- window->GetTabletData()->Pressure = axis_value / ((float)m_xtablet.PressureLevels);
- }
+ if (AXIS_VALUE_GET(2, axis_value)) {
+ window->GetTabletData()->Pressure = axis_value / ((float)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
- *
- * The '& 0xffff' just truncates the value to its two lowest bytes, this probably means
- * some drivers do not properly set the whole int value? Since we convert to float afterward,
- * I don't think we need to cast to short here, but do not have a device to check this. --mont29
- */
- if (AXIS_VALUE_GET(3, axis_value)) {
- window->GetTabletData()->Xtilt = (short)(axis_value & 0xffff) /
- ((float)m_xtablet.XtiltLevels);
- }
- if (AXIS_VALUE_GET(4, axis_value)) {
- window->GetTabletData()->Ytilt = (short)(axis_value & 0xffff) /
- ((float)m_xtablet.YtiltLevels);
- }
+ /* 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
+ *
+ * The '& 0xffff' just truncates the value to its two lowest bytes, this probably means
+ * some drivers do not properly set the whole int value? Since we convert to float afterward,
+ * I don't think we need to cast to short here, but do not have a device to check this. --mont29
+ */
+ if (AXIS_VALUE_GET(3, axis_value)) {
+ window->GetTabletData()->Xtilt = (short)(axis_value & 0xffff) /
+ ((float)xtablet.XtiltLevels);
+ }
+ if (AXIS_VALUE_GET(4, axis_value)) {
+ window->GetTabletData()->Ytilt = (short)(axis_value & 0xffff) /
+ ((float)xtablet.YtiltLevels);
+ }
#undef AXIS_VALUE_GET
- }
- else if (xe->type == m_xtablet.ProxInEvent) {
- XProximityNotifyEvent *data = (XProximityNotifyEvent *)xe;
+ }
+ else if (xe->type == xtablet.ProxInEvent) {
+ XProximityNotifyEvent *data = (XProximityNotifyEvent *)xe;
+ if (data->deviceid != xtablet.ID) {
+ continue;
+ }
- setTabletMode(this, window, data->deviceid);
- }
- else if (xe->type == m_xtablet.ProxOutEvent) {
- window->GetTabletData()->Active = GHOST_kTabletModeNone;
+ window->GetTabletData()->Active = xtablet.mode;
+ }
+ else if (xe->type == xtablet.ProxOutEvent) {
+ window->GetTabletData()->Active = GHOST_kTabletModeNone;
+ }
}
#endif // WITH_X11_XINPUT
break;
@@ -2304,6 +2297,20 @@ static GHOST_TTabletMode tablet_mode_from_name(const char *name, const char *typ
NULL
};
+ static const char* type_blacklist[] = {
+ "pad",
+ "cursor",
+ "touch",
+ NULL
+ };
+
+ /* Skip some known unsupported types. */
+ for (i=0; type_blacklist[i] != NULL; i++) {
+ if (type && (strcasecmp(type, type_blacklist[i]) == 0)) {
+ return GHOST_kTabletModeNone;
+ }
+ }
+
/* First check device type to avoid cases where name is "Pen and Eraser" and type is "ERASER" */
for (i=0; tablet_stylus_whitelist[i] != NULL; i++) {
if (type && match_token(type, tablet_stylus_whitelist[i])) {
@@ -2330,16 +2337,8 @@ static GHOST_TTabletMode tablet_mode_from_name(const char *name, const char *typ
void GHOST_SystemX11::refreshXInputDevices()
{
if (m_xinput_version.present) {
-
- if (m_xtablet.StylusDevice) {
- XCloseDevice(m_display, m_xtablet.StylusDevice);
- m_xtablet.StylusDevice = NULL;
- }
-
- if (m_xtablet.EraserDevice) {
- XCloseDevice(m_display, m_xtablet.EraserDevice);
- m_xtablet.EraserDevice = NULL;
- }
+ /* Close tablet devices. */
+ clearXInputDevices();
/* Install our error handler to override Xlib's termination behavior */
GHOST_X11_ERROR_HANDLERS_OVERRIDE(handler_store);
@@ -2351,72 +2350,51 @@ void GHOST_SystemX11::refreshXInputDevices()
for (int i = 0; i < device_count; ++i) {
char *device_type = device_info[i].type ? XGetAtomName(m_display, device_info[i].type) : NULL;
+ GHOST_TTabletMode tablet_mode = tablet_mode_from_name(device_info[i].name, device_type);
// printf("Tablet type:'%s', name:'%s', index:%d\n", device_type, device_info[i].name, i);
- GHOST_TTabletMode tablet_mode = tablet_mode_from_name(device_info[i].name, device_type);
+ if (device_type) {
+ XFree((void *)device_type);
+ }
- if ((m_xtablet.StylusDevice == NULL) && (tablet_mode == GHOST_kTabletModeStylus)) {
-// 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;
- bool found_valuator_class = false;
-
- 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;
-
- if (xvi->num_axes > 3) {
- /* 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;
- }
- else {
- m_xtablet.XtiltLevels = 0;
- m_xtablet.YtiltLevels = 0;
- }
+ if (!(tablet_mode == GHOST_kTabletModeStylus || tablet_mode == GHOST_kTabletModeEraser)) {
+ continue;
+ }
- found_valuator_class = (m_xtablet.PressureLevels > 0);
+ GHOST_TabletX11 xtablet = {tablet_mode};
+ xtablet.ID = device_info[i].id;
+ xtablet.Device = XOpenDevice(m_display, xtablet.ID);
- break;
+ if (xtablet.Device != NULL) {
+ /* Find how many pressure levels tablet has */
+ XAnyClassPtr ici = device_info[i].inputclassinfo;
+
+ for (int j = 0; j < xtablet.Device->num_classes; ++j) {
+ if (ici->c_class == ValuatorClass) {
+ XValuatorInfo *xvi = (XValuatorInfo *)ici;
+ xtablet.PressureLevels = xvi->axes[2].max_value;
+
+ if (xvi->num_axes > 3) {
+ /* 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.. */
+ xtablet.XtiltLevels = xvi->axes[3].max_value;
+ xtablet.YtiltLevels = xvi->axes[4].max_value;
+ }
+ else {
+ xtablet.XtiltLevels = 0;
+ xtablet.YtiltLevels = 0;
}
- ici = (XAnyClassPtr)(((char *)ici) + ici->length);
+ break;
}
- if (!found_valuator_class) {
- /* In case our name matching detects a device that
- * isn't actually a stylus. For example there can
- * be "XPPEN Tablet" and "XPPEN Tablet Pen", but
- * only the latter is a stylus. */
- XCloseDevice(m_display, m_xtablet.StylusDevice);
- m_xtablet.StylusDevice = NULL;
- m_xtablet.StylusID = 0;
- }
+ ici = (XAnyClassPtr)(((char *)ici) + ici->length);
}
- else {
- m_xtablet.StylusID = 0;
- }
- }
- else if ((m_xtablet.EraserDevice == NULL) &&
- (tablet_mode == GHOST_kTabletModeEraser))
- {
-// 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);
+ m_xtablets.push_back(xtablet);
}
+
}
XFreeDeviceList(device_info);
@@ -2426,4 +2404,14 @@ void GHOST_SystemX11::refreshXInputDevices()
}
}
+void GHOST_SystemX11::clearXInputDevices()
+{
+ for (GHOST_TabletX11& xtablet: m_xtablets) {
+ if (xtablet.Device)
+ XCloseDevice(m_display, xtablet.Device);
+ }
+
+ m_xtablets.clear();
+}
+
#endif /* WITH_X11_XINPUT */
diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h
index aaed0c3d837..a4c997c7bf9 100644
--- a/intern/ghost/intern/GHOST_SystemX11.h
+++ b/intern/ghost/intern/GHOST_SystemX11.h
@@ -309,28 +309,22 @@ public:
#ifdef WITH_X11_XINPUT
typedef struct GHOST_TabletX11 {
- XDevice *StylusDevice;
- XDevice *EraserDevice;
-
- XID StylusID, EraserID;
+ GHOST_TTabletMode mode;
+ XDevice *Device;
+ XID ID;
int MotionEvent;
int ProxInEvent;
int ProxOutEvent;
int PressEvent;
- int MotionEventEraser;
- int ProxInEventEraser;
- int ProxOutEventEraser;
- int PressEventEraser;
-
int PressureLevels;
int XtiltLevels, YtiltLevels;
} GHOST_TabletX11;
- GHOST_TabletX11 &GetXTablet()
+ std::vector<GHOST_TabletX11> &GetXTablets()
{
- return m_xtablet;
+ return m_xtablets;
}
#endif // WITH_X11_XINPUT
@@ -384,7 +378,7 @@ private:
#ifdef WITH_X11_XINPUT
/* Tablet devices */
- GHOST_TabletX11 m_xtablet;
+ std::vector<GHOST_TabletX11> m_xtablets;
#endif
/// The vector of windows that need to be updated.
@@ -414,6 +408,7 @@ private:
#endif
#ifdef WITH_X11_XINPUT
+ void clearXInputDevices();
void refreshXInputDevices();
#endif
diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp
index 01b4991ec72..3751f7f78ea 100644
--- a/intern/ghost/intern/GHOST_WindowX11.cpp
+++ b/intern/ghost/intern/GHOST_WindowX11.cpp
@@ -648,37 +648,26 @@ bool GHOST_WindowX11::createX11_XIC()
void GHOST_WindowX11::refreshXInputDevices()
{
if (m_system->m_xinput_version.present) {
- GHOST_SystemX11::GHOST_TabletX11 &xtablet = m_system->GetXTablet();
- XEventClass xevents[8], ev;
- int dcount = 0;
-
- /* With modern XInput (xlib 1.6.2 at least and/or evdev 2.9.0) and some 'no-name' tablets
- * like 'UC-LOGIC Tablet WP5540U', we also need to 'select' ButtonPress for motion event,
- * otherwise we do not get any tablet motion event once pen is pressed... See T43367.
- */
-
- if (xtablet.StylusDevice) {
- DeviceMotionNotify(xtablet.StylusDevice, xtablet.MotionEvent, ev);
- if (ev) xevents[dcount++] = ev;
- DeviceButtonPress(xtablet.StylusDevice, xtablet.PressEvent, ev);
- if (ev) xevents[dcount++] = ev;
- ProximityIn(xtablet.StylusDevice, xtablet.ProxInEvent, ev);
- if (ev) xevents[dcount++] = ev;
- ProximityOut(xtablet.StylusDevice, xtablet.ProxOutEvent, ev);
- if (ev) xevents[dcount++] = ev;
- }
- if (xtablet.EraserDevice) {
- DeviceMotionNotify(xtablet.EraserDevice, xtablet.MotionEventEraser, ev);
- if (ev) xevents[dcount++] = ev;
- DeviceButtonPress(xtablet.EraserDevice, xtablet.PressEventEraser, ev);
- if (ev) xevents[dcount++] = ev;
- ProximityIn(xtablet.EraserDevice, xtablet.ProxInEventEraser, ev);
- if (ev) xevents[dcount++] = ev;
- ProximityOut(xtablet.EraserDevice, xtablet.ProxOutEventEraser, ev);
- if (ev) xevents[dcount++] = ev;
+ std::vector<XEventClass> xevents;
+
+ for (GHOST_SystemX11::GHOST_TabletX11& xtablet: m_system->GetXTablets()) {
+ /* With modern XInput (xlib 1.6.2 at least and/or evdev 2.9.0) and some 'no-name' tablets
+ * like 'UC-LOGIC Tablet WP5540U', we also need to 'select' ButtonPress for motion event,
+ * otherwise we do not get any tablet motion event once pen is pressed... See T43367.
+ */
+ XEventClass ev;
+
+ DeviceMotionNotify(xtablet.Device, xtablet.MotionEvent, ev);
+ if (ev) xevents.push_back(ev);
+ DeviceButtonPress(xtablet.Device, xtablet.PressEvent, ev);
+ if (ev) xevents.push_back(ev);
+ ProximityIn(xtablet.Device, xtablet.ProxInEvent, ev);
+ if (ev) xevents.push_back(ev);
+ ProximityOut(xtablet.Device, xtablet.ProxOutEvent, ev);
+ if (ev) xevents.push_back(ev);
}
- XSelectExtensionEvent(m_display, m_window, xevents, dcount);
+ XSelectExtensionEvent(m_display, m_window, xevents.data(), (int)xevents.size());
}
}