Re: Huion tablet support

Yorick Hardy <[email protected]>
Newsgroups gmane.os.netbsd.devel.kernel
Message-ID <[email protected]>
On 2025-02-19, adr wrote:
> Hi.
> 
> I'm going to do some work adding support for the drawing tablets I
> discussed here:
> 
> https://mail-index.netbsd.org/netbsd-users/2023/05/14/msg029686.html
> 
> These devices use chips called uclogic that are found on many other
> devices.
> 
> These devices use the same vendor:product id for different models,
> don't conform to the hid standard, strings descriptors have to be
> read to obtain parameters and set the tablet mode (full, phone).
> These strings are different between models. Trying to make a general
> algorithm to manage all models is not a great idea in my opinion.
> Look at the linux driver:
> 
> https://raw.githubusercontent.com/DIGImend/digimend-kernel-drivers/master/hid-uclogic-params.c
> 
> They try first as if the device were an old one, then gess if it is
> wrong and try another way, and all of this making exeptions for
> different models anyway. And this driver hasn't been written by a
> vendor, there is no certainty that the next chip will make something
> different, for example the codification of the parameters in the
> string descriptor, so the code has to be kneaded again... I really
> don't like it.
> 
> In sort what I did basically was adding some code in uhidev.c and
> supporting files to set the device in the adecuate mode and set
> the descriptor report for the corresponding model (I made it easy
> to add new models and reuse if possible the structures, &c)
> 
> The problem is that pressure is not managed by ums(4).
> 
> I've been using the old usbtablet driver still maintained in openbsd.
> 
> I don't like this workaround:
> 
> 1. The driver is not in netbsd's xorg.
> 2. Deattaching the device will freeze the system.
> 3. With wscons support, it will work in whatever environment that uses
> wscons, not just xorg.
> 
> This could bring support to netbsd for a lot of graphics tablets,
> these chips are use by several manufactures (Huion, Yiynova, Ugee,
> Monoprice, Turcom...).
> 
> One cheap solution I'm seeing is to check in ums.c for HUD_TIP_PRESSURE
> at ums_attach(), when hid_start_parse() is used for calibration,
> then change hidms_loc_z to match that of pressure.
> 
> A similar check should be done in sys/dev/hid/hidms.c:hidms_intr()
> to add the flag WSMOUSE_INPUT_ABSOLUTE_Z. Would be a good idea
> to check for the HIDMS_DIGITIZER flag and assume that z will be
> absolute?
> 
> If devices have problems with that, the check could be narrow to
> a list of devices instead, but I doubt those devices could work
> correctly with wscons anyway...
> 
> Some thoughts or guidance before I start digging in?
> 
> Regards.
> adr.

For pressure on a Wacom Intuos -- on the Xorg side of things -- I
have been using the attached patch for many years. It abuses the Z
input for pressure, so probably not what we want commited; but
perhaps it provides a starting point.

-- 
Kind regards,

Yorick Hardy
xsrc-wsmouse.patch (text/x-diff, 10.3 KB)
Index: external/mit/xf86-input-ws/dist/include/ws-properties.h
===================================================================
RCS file: /cvsroot/xsrc/external/mit/xf86-input-ws/dist/include/ws-properties.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 ws-properties.h
--- external/mit/xf86-input-ws/dist/include/ws-properties.h	27 Nov 2009 14:17:19 -0000	1.1.1.1
+++ external/mit/xf86-input-ws/dist/include/ws-properties.h	16 Jul 2022 21:31:52 -0000
@@ -27,6 +27,10 @@
 /* CARD32, 4 values [minx, maxx, miny, maxy], or no values for unset */
 #define WS_PROP_CALIBRATION "WS Pointer Axis Calibration"
 
+/* Run-time pressure calibration */
+/* CARD32, 2 values [minz, maxz], or no values for unset */
+#define WS_PROP_PRESSURE "WS Pointer Pressure Calibration"
+
 /* Swap x and y axis. */
 /* BOOL */
 #define WS_PROP_SWAP_AXES "WS Pointer Axes Swap"
Index: external/mit/xf86-input-ws/dist/man/ws.man
===================================================================
RCS file: /cvsroot/xsrc/external/mit/xf86-input-ws/dist/man/ws.man,v
retrieving revision 1.3
diff -u -r1.3 ws.man
--- external/mit/xf86-input-ws/dist/man/ws.man	2 Aug 2011 09:30:15 -0000	1.3
+++ external/mit/xf86-input-ws/dist/man/ws.man	16 Jul 2022 21:31:52 -0000
@@ -143,6 +143,9 @@
 1 boolean value (8 bits, 0 or 1). 1 swaps x/y axes. This property
 is present only for devices with absolute coordinates (ie
 tablets and touchscreens).
+.TP 7
+.BI "WS Pointer Pressure Calibration"
+2 32 bits values, in the order min-z, max-z.
 
 .SH "SEE ALSO"
 __xservername__(1), xinput(1), __xconfigfile__(__filemansuffix__),
Index: external/mit/xf86-input-ws/dist/src/ws.c
===================================================================
RCS file: /cvsroot/xsrc/external/mit/xf86-input-ws/dist/src/ws.c,v
retrieving revision 1.16
diff -u -r1.16 ws.c
--- external/mit/xf86-input-ws/dist/src/ws.c	4 Dec 2021 15:21:55 -0000	1.16
+++ external/mit/xf86-input-ws/dist/src/ws.c	16 Jul 2022 21:31:52 -0000
@@ -66,6 +66,7 @@
 static int wsSetProperty(DeviceIntPtr, Atom, XIPropertyValuePtr, BOOL);
 
 static Atom prop_calibration = 0;
+static Atom prop_pressure = 0;
 static Atom prop_swap = 0;
 
 #ifdef DEBUG
@@ -271,7 +272,13 @@
 	priv->max_x = screenInfo.screens[priv->screen_no]->width - 1;
 	priv->min_y = 0;
 	priv->max_y = screenInfo.screens[priv->screen_no]->height - 1;
+	priv->min_z = -1;
+	priv->max_z = -1;
+	priv->min_w = -1;
+	priv->max_w = -1;
 	priv->raw = 0;
+	/* Allow options to override this */
+	priv->raw = xf86SetBoolOption(pInfo->options, "Raw", priv->raw);
 
 	/* don't rely on the device type - we may be listening to a mux */		
 	if (ioctl(pInfo->fd, WSMOUSEIO_GCALIBCOORDS,
@@ -314,6 +321,8 @@
 		xf86Msg(X_CONFIG,
 		    "%s device will work in raw mode\n",
 		    pInfo->name);
+		/* Assume a touch panel / tablet since we are in raw mode */
+		priv->type = WSMOUSE_TYPE_TPANEL;
 	}
 
 	/* Allow options to override this */
@@ -329,6 +338,18 @@
 	priv->max_y = xf86SetIntOption(pInfo->options, "MaxY", priv->max_y);
 	xf86Msg(X_INFO, "%s maximum y position: %d\n",
 	    pInfo->name, priv->max_y);
+	priv->min_z = xf86SetIntOption(pInfo->options, "MinZ", priv->min_z);
+	xf86Msg(X_INFO, "%s minimum z position: %d\n",
+	    pInfo->name, priv->min_z);
+	priv->max_z = xf86SetIntOption(pInfo->options, "MaxZ", priv->max_z);
+	xf86Msg(X_INFO, "%s maximum z position: %d\n",
+	    pInfo->name, priv->max_z);
+	priv->min_w = xf86SetIntOption(pInfo->options, "MinW", priv->min_w);
+	xf86Msg(X_INFO, "%s minimum w position: %d\n",
+	    pInfo->name, priv->min_w);
+	priv->max_w = xf86SetIntOption(pInfo->options, "MaxW", priv->max_w);
+	xf86Msg(X_INFO, "%s maximum w position: %d\n",
+	    pInfo->name, priv->max_w);
 
 	pInfo->device_control = wsProc;
 	pInfo->read_input = wsReadInput;
@@ -417,7 +438,7 @@
 	InputInfoPtr pInfo = (InputInfoPtr)pWS->public.devicePrivate;
 	WSDevicePtr priv = (WSDevicePtr)pInfo->private;
 	unsigned char map[NBUTTONS + 1];
-	int i, xmin, xmax, ymin, ymax;
+	int i, xmin, xmax, ymin, ymax, zmin, zmax, wmin, wmax;
 	Atom btn_labels[NBUTTONS] = {0};
 	Atom axes_labels[NAXES] = {0};
 
@@ -446,6 +467,11 @@
 		ymax = -1;
 	}
 
+	zmin = priv->min_z;
+	zmax = priv->max_z;
+	wmin = priv->min_w;
+	wmax = priv->max_w;
+
 	if (priv->swap_axes) {
 		int tmp;
 		tmp = xmin;
@@ -458,9 +484,13 @@
 	if ((priv->type == WSMOUSE_TYPE_TPANEL)) {
 		axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X);
 		axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y);
+		axes_labels[2] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_PRESSURE);
+		axes_labels[3] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_DISTANCE);
 	} else {
 		axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X);
 		axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y);
+		axes_labels[2] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Z);
+		axes_labels[3] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_WHEEL);
 	}
 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 14
 	axes_labels[HSCROLL_AXIS] =
@@ -496,6 +526,24 @@
 	);
 	xf86InitValuatorDefaults(pWS, 1);
 
+	xf86InitValuatorAxisStruct(pWS, 2,
+	    axes_labels[2],
+	    zmin, zmax, 1, 0, 1
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+	    , priv->type == WSMOUSE_TYPE_TPANEL  ? Absolute : Relative
+#endif
+	);
+	xf86InitValuatorDefaults(pWS, 2);
+
+	xf86InitValuatorAxisStruct(pWS, 3,
+	    axes_labels[3],
+	    wmin, wmax, 1, 0, 1
+#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
+	    , priv->type == WSMOUSE_TYPE_TPANEL ? Absolute : Relative
+#endif
+	);
+	xf86InitValuatorDefaults(pWS, 3);
+
 
 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 14
 	xf86InitValuatorAxisStruct(pWS, HSCROLL_AXIS,
@@ -621,7 +669,7 @@
 	int n, c;
 	struct wscons_event *event = eventList;
 	unsigned char *pBuf;
-	int ax, ay;
+	int ax, ay, az, aw;
 
 	priv = pInfo->private;
 
@@ -636,6 +684,7 @@
 		return;
 
 	n /= sizeof(struct wscons_event);
+	ax = 0; ay = 0; az = 0; aw = 0;
 	while( n-- ) {
 		int buttons = priv->lastButtons;
 		int dx = 0, dy = 0, dz = 0, dw = 0;
@@ -644,7 +693,6 @@
 		int hscroll = 0, vscroll = 0;
 #endif
 
-		ax = 0; ay = 0;
 		switch (event->type) {
 		case WSCONS_EVENT_MOUSE_UP:
 
@@ -684,14 +732,15 @@
 			dz = event->value;
 			break;
 		case WSCONS_EVENT_MOUSE_ABSOLUTE_Z:
-			/* ignore those */
-			++event;
-			continue;
+			az = event->value;
 			break;
 		case WSCONS_EVENT_MOUSE_DELTA_W:
 			DBG(4, ErrorF("Relative W %d\n", event->value));
 			dw = event->value;
 			break;
+		case WSCONS_EVENT_MOUSE_ABSOLUTE_W:
+			aw = event->value;
+			break;
 #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 14
 		case WSCONS_EVENT_HSCROLL:
 			hscroll = event->value;
@@ -774,18 +823,42 @@
 			ax = ay;
 			ay = tmp;
 		}
-		if (ax) {
-			/* absolute position event */
-			DBG(3, ErrorF("postMotionEvent X %d\n", ax));
-			xf86PostMotionEvent(pInfo->dev, 1, 0, 1, ax);
-		}
-		if (ay) {
-			/* absolute position event */
-			DBG(3, ErrorF("postMotionEvent y %d\n", ay));
-			xf86PostMotionEvent(pInfo->dev, 1, 1, 1, ay);
-		}
 		++event;
 	}
+
+	/*
+	 * Report movement after looking at all events,
+	 * so that x-y motion can be reported together.
+	 *
+	 * There must be a better way to check x, y (and z) combinations?
+	 * Should dx and dy also be posted together?
+	 */
+	if (ax && ay) {
+		/* absolute position event */
+		DBG(3, ErrorF("postMotionEvent X %d\n", ax));
+		xf86PostMotionEvent(pInfo->dev, 1, 0, 2, ax, ay);
+	}
+	if (ax && !ay) {
+		/* absolute position event */
+		DBG(3, ErrorF("postMotionEvent X %d\n", ax));
+		xf86PostMotionEvent(pInfo->dev, 1, 0, 1, ax);
+	}
+	if (ay && !ax) {
+		/* absolute position event */
+		DBG(3, ErrorF("postMotionEvent y %d\n", ay));
+		xf86PostMotionEvent(pInfo->dev, 1, 1, 1, ay);
+	}
+	if (az) {
+		/* absolute position event */
+		DBG(3, ErrorF("postMotionEvent Z %d\n", az));
+		xf86PostMotionEvent(pInfo->dev, 1, 2, 1, az);
+	}
+	if (aw) {
+		/* absolute position event */
+		DBG(3, ErrorF("postMotionEvent W %d\n", aw));
+		xf86PostMotionEvent(pInfo->dev, 1, 3, 1, aw);
+	}
+
 	return;
 } /* wsReadInput */
 
@@ -887,6 +960,13 @@
 
 	XISetDevicePropertyDeletable(device, prop_calibration, FALSE);
 
+	prop_pressure = MakeAtom(WS_PROP_PRESSURE,
+	    strlen(WS_PROP_PRESSURE), TRUE);
+	rc = XIChangeDeviceProperty(device, prop_pressure, XA_INTEGER, 32,
+	    PropModeReplace, 2, &priv->min_z, FALSE);
+	if (rc != Success)
+		return;
+
 	prop_swap = MakeAtom(WS_PROP_SWAP_AXES,
 	    strlen(WS_PROP_SWAP_AXES), TRUE);
 	rc = XIChangeDeviceProperty(device, prop_swap, XA_INTEGER, 8,
@@ -905,7 +985,8 @@
 	struct wsmouse_calibcoords coords;
 	int need_update = 0;
 	AxisInfoPtr ax = device->valuator->axes,
-		    ay = device->valuator->axes + 1;
+		    ay = device->valuator->axes + 1,
+		    az = device->valuator->axes + 2;
 
 	DBG(1, ErrorF("wsSetProperty %s\n", NameForAtom(atom)));
 
@@ -948,6 +1029,26 @@
 				ay->max_value = priv->max_x;
 			}
 		}
+	} else if (atom == prop_pressure) {
+		if (val->format != 32 || val->type != XA_INTEGER)
+			return BadMatch;
+		if (val->size != 2 && val->size != 0)
+			return BadMatch;
+		if (!checkonly) {
+			if (val->size == 0) {
+				DBG(1, ErrorF(" uncalibrate pressure\n"));
+				priv->min_z = 0;
+				priv->max_z = -1;
+			} else {
+				priv->min_z = ((int *)(val->data))[0];
+				priv->max_z = ((int *)(val->data))[1];
+				DBG(1, ErrorF(" calibrate pressure %d %d\n",
+					priv->min_z, priv->max_z));
+			}
+			/* Update axes descriptors */
+			az->min_value = priv->min_z;
+			az->max_value = priv->max_z;
+		}
 	} else if (atom == prop_swap) {
 		if (val->format != 8 || val->type != XA_INTEGER ||
 		    val->size != 1)
Index: external/mit/xf86-input-ws/dist/src/ws.h
===================================================================
RCS file: /cvsroot/xsrc/external/mit/xf86-input-ws/dist/src/ws.h,v
retrieving revision 1.6
diff -u -r1.6 ws.h
--- external/mit/xf86-input-ws/dist/src/ws.h	4 Dec 2021 15:21:55 -0000	1.6
+++ external/mit/xf86-input-ws/dist/src/ws.h	16 Jul 2022 21:31:52 -0000
@@ -27,9 +27,9 @@
 # define DBG(lvl, f)
 #endif
 
-#define NAXES 4			/* X and Y, horizontal and vertical scrolling */
-#define HSCROLL_AXIS	2
-#define VSCROLL_AXIS	3
+#define NAXES 6			/* X, Y, Z, W and horizontal and vertical scrolling */
+#define HSCROLL_AXIS	4
+#define VSCROLL_AXIS	5
 
 #define NBUTTONS 32		/* max theoretical buttons */
 #define DFLTBUTTONS 3		/* default number of buttons */
@@ -41,6 +41,7 @@
 	unsigned int buttons;	/* # of buttons */
 	unsigned int lastButtons; /* last state of buttons */
 	int min_x, max_x, min_y, max_y; /* coord space */
+	int min_z, max_z, min_w, max_w; /* coord space */
 	int swap_axes;
 	int raw;
 	int inv_x, inv_y;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.