1 /*
2  * Copyright 2007-2010 by Ping Cheng, Wacom. <pingc@wacom.com>
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
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 #include <wacom-properties.h>
23 
24 #include "xf86Wacom.h"
25 #include "wcmFilter.h"
26 #include <exevents.h>
27 #include <xf86_OSproc.h>
28 
29 #ifndef XI_PROP_DEVICE_NODE
30 #define XI_PROP_DEVICE_NODE "Device Node"
31 #endif
32 #ifndef XI_PROP_PRODUCT_ID
33 #define XI_PROP_PRODUCT_ID "Device Product ID"
34 #endif
35 
36 static void wcmBindToSerial(InputInfoPtr pInfo, unsigned int serial);
37 
38 /*****************************************************************************
39 * wcmDevSwitchModeCall --
40 *****************************************************************************/
41 
wcmDevSwitchModeCall(InputInfoPtr pInfo,int mode)42 int wcmDevSwitchModeCall(InputInfoPtr pInfo, int mode)
43 {
44 	WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
45 
46 	DBG(3, priv, "to mode=%d\n", mode);
47 
48 	/* Pad is always in absolute mode.*/
49 	if (IsPad(priv))
50 		return (mode == Absolute) ? Success : XI_BadMode;
51 
52 	if ((mode == Absolute) && !is_absolute(pInfo))
53 		set_absolute(pInfo, TRUE);
54 	else if ((mode == Relative) && is_absolute(pInfo))
55 		set_absolute(pInfo, FALSE);
56 	else if ( (mode != Absolute) && (mode != Relative))
57 	{
58 		DBG(10, priv, "invalid mode=%d\n", mode);
59 		return XI_BadMode;
60 	}
61 
62 	return Success;
63 }
64 
65 /*****************************************************************************
66 * wcmDevSwitchMode --
67 *****************************************************************************/
68 
wcmDevSwitchMode(ClientPtr client,DeviceIntPtr dev,int mode)69 int wcmDevSwitchMode(ClientPtr client, DeviceIntPtr dev, int mode)
70 {
71 	InputInfoPtr pInfo = (InputInfoPtr)dev->public.devicePrivate;
72 #ifdef DEBUG
73 	WacomDevicePtr priv = (WacomDevicePtr)pInfo->private;
74 
75 	DBG(3, priv, "dev=%p mode=%d\n",
76 		(void *)dev, mode);
77 #endif
78 	/* Share this call with sendAButton in wcmCommon.c */
79 	return wcmDevSwitchModeCall(pInfo, mode);
80 }
81 
82 static Atom prop_devnode;
83 static Atom prop_rotation;
84 static Atom prop_tablet_area;
85 static Atom prop_pressurecurve;
86 static Atom prop_serials;
87 static Atom prop_serial_binding;
88 static Atom prop_strip_buttons;
89 static Atom prop_wheel_buttons;
90 static Atom prop_proxout;
91 static Atom prop_threshold;
92 static Atom prop_suppress;
93 static Atom prop_touch;
94 static Atom prop_hardware_touch;
95 static Atom prop_gesture;
96 static Atom prop_gesture_param;
97 static Atom prop_hover;
98 static Atom prop_tooltype;
99 static Atom prop_btnactions;
100 static Atom prop_product_id;
101 static Atom prop_pressure_recal;
102 static Atom prop_panscroll_threshold;
103 #ifdef DEBUG
104 static Atom prop_debuglevels;
105 #endif
106 
107 /**
108  * Calculate a user-visible pressure level from a driver-internal pressure
109  * level. Pressure settings exposed to the user assume a range of 0-2047
110  * while the driver scales everything to a range of 0-maxCurve.
111  */
wcmInternalToUserPressure(InputInfoPtr pInfo,int pressure)112 static inline int wcmInternalToUserPressure(InputInfoPtr pInfo, int pressure)
113 {
114 	WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
115 	return pressure / (priv->maxCurve / 2048);
116 }
117 
118 /**
119  * Calculate a driver-internal pressure level from a user-visible pressure
120  * level. Pressure settings exposed to the user assume a range of 0-2047
121  * while the driver scales everything to a range of 0-maxCurve.
122  */
wcmUserToInternalPressure(InputInfoPtr pInfo,int pressure)123 static inline int wcmUserToInternalPressure(InputInfoPtr pInfo, int pressure)
124 {
125 	WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
126 	return pressure * (priv->maxCurve / 2048);
127 }
128 
129 /**
130  * Resets an arbitrary Action property, given a pointer to the old
131  * handler and information about the new Action.
132  */
wcmResetAction(InputInfoPtr pInfo,const char * name,int index,Atom * handler,unsigned int (* action)[256],unsigned int (* new_action)[256],Atom prop,int nprop)133 static void wcmResetAction(InputInfoPtr pInfo, const char *name, int index,
134                            Atom *handler, unsigned int (*action)[256],
135                            unsigned int (*new_action)[256], Atom prop, int nprop)
136 {
137 	handler[index] = MakeAtom(name, strlen(name), TRUE);
138 	memset(action[index], 0, sizeof(action[index]));
139 	memcpy(action[index], *new_action, sizeof(*new_action));
140 	XIChangeDeviceProperty(pInfo->dev, handler[index], XA_INTEGER, 32,
141 			       PropModeReplace, 1, (char*)new_action, FALSE);
142 }
143 
wcmResetButtonAction(InputInfoPtr pInfo,int button,int nbuttons)144 static void wcmResetButtonAction(InputInfoPtr pInfo, int button, int nbuttons)
145 {
146 	WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
147 	unsigned int new_action[256] = {};
148 	int x11_button = priv->button_default[button];
149 	char name[64];
150 
151 	sprintf(name, "Wacom button action %d", button);
152 	new_action[0] = AC_BUTTON | AC_KEYBTNPRESS | x11_button;
153 	wcmResetAction(pInfo, name, button, priv->btn_actions, priv->keys, &new_action, prop_btnactions, nbuttons);
154 }
155 
wcmResetStripAction(InputInfoPtr pInfo,int index)156 static void wcmResetStripAction(InputInfoPtr pInfo, int index)
157 {
158 	WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
159 	unsigned int new_action[256] = {};
160 	char name[64];
161 
162 	sprintf(name, "Wacom strip action %d", index);
163 	new_action[0] =	AC_BUTTON | AC_KEYBTNPRESS | (priv->strip_default[index]);
164 	new_action[1] = AC_BUTTON | (priv->strip_default[index]);
165 	wcmResetAction(pInfo, name, index, priv->strip_actions, priv->strip_keys, &new_action, prop_strip_buttons, 4);
166 }
167 
wcmResetWheelAction(InputInfoPtr pInfo,int index)168 static void wcmResetWheelAction(InputInfoPtr pInfo, int index)
169 {
170 	WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
171 	unsigned int new_action[256] = {};
172 	char name[64];
173 
174 	sprintf(name, "Wacom wheel action %d", index);
175 	new_action[0] = AC_BUTTON | AC_KEYBTNPRESS | (priv->wheel_default[index]);
176 	new_action[1] = AC_BUTTON | (priv->wheel_default[index]);
177 	wcmResetAction(pInfo, name, index, priv->wheel_actions, priv->wheel_keys, &new_action, prop_wheel_buttons, 6);
178 }
179 
180 /**
181  * Registers a property for the input device. This function registers
182  * the property name atom, as well as creates the property itself.
183  * At creation, the property values are initialized from the 'values'
184  * array. The device property is marked as non-deletable.
185  * Initialization values are always to be provided by means of an
186  * array of 32 bit integers, regardless of 'format'
187  *
188  * @param dev Pointer to device structure
189  * @param name Name of device property
190  * @param type Type of the property
191  * @param format Format of the property (8/16/32)
192  * @param nvalues Number of values in the property
193  * @param values Pointer to 32 bit integer array of initial property values
194  * @return Atom handle of property name
195  */
InitWcmAtom(DeviceIntPtr dev,const char * name,Atom type,int format,int nvalues,int * values)196 static Atom InitWcmAtom(DeviceIntPtr dev, const char *name, Atom type, int format, int nvalues, int *values)
197 {
198 	int i;
199 	Atom atom;
200 	uint8_t val_8[WCM_MAX_BUTTONS];
201 	uint16_t val_16[WCM_MAX_BUTTONS];
202 	uint32_t val_32[WCM_MAX_BUTTONS];
203 	pointer converted = val_32;
204 
205 	for (i = 0; i < nvalues; i++)
206 	{
207 		switch(format)
208 		{
209 			case 8:  val_8[i]  = values[i]; break;
210 			case 16: val_16[i] = values[i]; break;
211 			case 32: val_32[i] = values[i]; break;
212 		}
213 	}
214 
215 	switch(format)
216 	{
217 		case 8: converted = val_8; break;
218 		case 16: converted = val_16; break;
219 		case 32: converted = val_32; break;
220 	}
221 
222 	atom = MakeAtom(name, strlen(name), TRUE);
223 	XIChangeDeviceProperty(dev, atom, type, format,
224 			PropModeReplace, nvalues,
225 			converted, FALSE);
226 	XISetDevicePropertyDeletable(dev, atom, FALSE);
227 	return atom;
228 }
229 
InitWcmDeviceProperties(InputInfoPtr pInfo)230 void InitWcmDeviceProperties(InputInfoPtr pInfo)
231 {
232 	WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
233 	WacomCommonPtr common = priv->common;
234 	int values[WCM_MAX_BUTTONS];
235 	int i;
236 
237 	DBG(10, priv, "\n");
238 
239 	prop_devnode = MakeAtom(XI_PROP_DEVICE_NODE, strlen(XI_PROP_DEVICE_NODE), TRUE);
240 	XIChangeDeviceProperty(pInfo->dev, prop_devnode, XA_STRING, 8,
241 				PropModeReplace, strlen(common->device_path),
242 				common->device_path, FALSE);
243 	XISetDevicePropertyDeletable(pInfo->dev, prop_devnode, FALSE);
244 
245 	if (!IsPad(priv)) {
246 		values[0] = priv->topX;
247 		values[1] = priv->topY;
248 		values[2] = priv->bottomX;
249 		values[3] = priv->bottomY;
250 		prop_tablet_area = InitWcmAtom(pInfo->dev, WACOM_PROP_TABLET_AREA, XA_INTEGER, 32, 4, values);
251 	}
252 
253 	values[0] = common->wcmRotate;
254 	if (!IsPad(priv)) {
255 		prop_rotation = InitWcmAtom(pInfo->dev, WACOM_PROP_ROTATION, XA_INTEGER, 8, 1, values);
256 	}
257 
258 	if (IsPen(priv) || IsTouch(priv)) {
259 		values[0] = priv->nPressCtrl[0];
260 		values[1] = priv->nPressCtrl[1];
261 		values[2] = priv->nPressCtrl[2];
262 		values[3] = priv->nPressCtrl[3];
263 		prop_pressurecurve = InitWcmAtom(pInfo->dev, WACOM_PROP_PRESSURECURVE, XA_INTEGER, 32, 4, values);
264 	}
265 
266 	values[0] = common->tablet_id;
267 	values[1] = priv->oldState.serial_num;
268 	values[2] = priv->oldState.device_id;
269 	values[3] = priv->cur_serial;
270 	values[4] = priv->cur_device_id;
271 	prop_serials = InitWcmAtom(pInfo->dev, WACOM_PROP_SERIALIDS, XA_INTEGER, 32, 5, values);
272 
273 	values[0] = priv->serial;
274 	prop_serial_binding = InitWcmAtom(pInfo->dev, WACOM_PROP_SERIAL_BIND, XA_INTEGER, 32, 1, values);
275 
276 	if (IsTablet(priv)) {
277 		values[0] = priv->wcmProxoutDist;
278 		prop_proxout = InitWcmAtom(pInfo->dev, WACOM_PROP_PROXIMITY_THRESHOLD, XA_INTEGER, 32, 1, values);
279 	}
280 
281 	values[0] = (!common->wcmMaxZ) ? 0 : common->wcmThreshold;
282 	values[0] = wcmInternalToUserPressure(pInfo, values[0]);
283 	prop_threshold = InitWcmAtom(pInfo->dev, WACOM_PROP_PRESSURE_THRESHOLD, XA_INTEGER, 32, 1, values);
284 
285 	values[0] = common->wcmSuppress;
286 	values[1] = common->wcmRawSample;
287 	prop_suppress = InitWcmAtom(pInfo->dev, WACOM_PROP_SAMPLE, XA_INTEGER, 32, 2, values);
288 
289 	values[0] = common->wcmTouch;
290 	prop_touch = InitWcmAtom(pInfo->dev, WACOM_PROP_TOUCH, XA_INTEGER, 8, 1, values);
291 
292 	if (common->wcmHasHWTouchSwitch && IsTouch(priv)) {
293 		values[0] = common->wcmHWTouchSwitchState;
294 		prop_hardware_touch = InitWcmAtom(pInfo->dev, WACOM_PROP_HARDWARE_TOUCH, XA_INTEGER, 8, 1, values);
295 	}
296 
297 	if (IsStylus(priv)) {
298 		values[0] = !common->wcmTPCButton;
299 		prop_hover = InitWcmAtom(pInfo->dev, WACOM_PROP_HOVER, XA_INTEGER, 8, 1, values);
300 	}
301 
302 	values[0] = common->wcmGesture;
303 	prop_gesture = InitWcmAtom(pInfo->dev, WACOM_PROP_ENABLE_GESTURE, XA_INTEGER, 8, 1, values);
304 
305 	values[0] = common->wcmGestureParameters.wcmZoomDistance;
306 	values[1] = common->wcmGestureParameters.wcmScrollDistance;
307 	values[2] = common->wcmGestureParameters.wcmTapTime;
308 	prop_gesture_param = InitWcmAtom(pInfo->dev, WACOM_PROP_GESTURE_PARAMETERS, XA_INTEGER, 32, 3, values);
309 
310 	values[0] = MakeAtom(pInfo->type_name, strlen(pInfo->type_name), TRUE);
311 	prop_tooltype = InitWcmAtom(pInfo->dev, WACOM_PROP_TOOL_TYPE, XA_ATOM, 32, 1, values);
312 
313 	memset(values, 0, sizeof(values));
314 	prop_btnactions = InitWcmAtom(pInfo->dev, WACOM_PROP_BUTTON_ACTIONS, XA_ATOM, 32, priv->nbuttons, values);
315 	for (i = 0; i < priv->nbuttons; i++)
316 		wcmResetButtonAction(pInfo, i, priv->nbuttons);
317 
318 	if (IsPad(priv)) {
319 		memset(values, 0, sizeof(values));
320 		prop_strip_buttons = InitWcmAtom(pInfo->dev, WACOM_PROP_STRIPBUTTONS, XA_ATOM, 32, 4, values);
321 		for (i = 0; i < 4; i++)
322 			wcmResetStripAction(pInfo, i);
323 	}
324 
325 	if (IsPad(priv) || IsCursor(priv))
326 	{
327 		memset(values, 0, sizeof(values));
328 		prop_wheel_buttons = InitWcmAtom(pInfo->dev, WACOM_PROP_WHEELBUTTONS, XA_ATOM, 32, 6, values);
329 		for (i = 0; i < 6; i++)
330 			wcmResetWheelAction(pInfo, i);
331 	}
332 
333 	if (IsStylus(priv) || IsEraser(priv)) {
334 		values[0] = common->wcmPressureRecalibration;
335 		prop_pressure_recal = InitWcmAtom(pInfo->dev,
336 						  WACOM_PROP_PRESSURE_RECAL,
337 						  XA_INTEGER, 8, 1, values);
338 	}
339 
340 	values[0] = common->wcmPanscrollThreshold;
341 	prop_panscroll_threshold = InitWcmAtom(pInfo->dev, WACOM_PROP_PANSCROLL_THRESHOLD, XA_INTEGER, 32, 1, values);
342 
343 	values[0] = common->vendor_id;
344 	values[1] = common->tablet_id;
345 	prop_product_id = InitWcmAtom(pInfo->dev, XI_PROP_PRODUCT_ID, XA_INTEGER, 32, 2, values);
346 
347 #ifdef DEBUG
348 	values[0] = priv->debugLevel;
349 	values[1] = common->debugLevel;
350 	prop_debuglevels = InitWcmAtom(pInfo->dev, WACOM_PROP_DEBUGLEVELS, XA_INTEGER, 8, 2, values);
351 #endif
352 }
353 
354 /* Returns the offset of the property in the list given. If the property is
355  * not found, a negative error code is returned. */
wcmFindProp(Atom property,Atom * prop_list,int nprops)356 static int wcmFindProp(Atom property, Atom *prop_list, int nprops)
357 {
358 	int i;
359 
360 	/* check all properties used for button actions */
361 	for (i = 0; i < nprops; i++)
362 		if (prop_list[i] == property)
363 			return i;
364 
365 	return -BadAtom;
366 }
367 
368 /**
369  * Obtain a pointer to the the handler and action list for a given Action
370  * property. This function searches the button, wheel, and strip property
371  * handler lists.
372  *
373  * @param priv          The device whose handler lists should be searched
374  * @param property      The Action property that should be searched for
375  * @param[out] handler  Returns a pointer to the property's handler
376  * @param[out] action   Returns a pointer to the property's action list
377  * @return              'true' if the property was found. Neither out parameter
378  *                      will be null if this is the case.
379  */
wcmFindActionHandler(WacomDevicePtr priv,Atom property,Atom ** handler,unsigned int (** action)[256])380 static BOOL wcmFindActionHandler(WacomDevicePtr priv, Atom property, Atom **handler, unsigned int (**action)[256])
381 {
382 	int offset;
383 
384 	offset = wcmFindProp(property, priv->btn_actions, ARRAY_SIZE(priv->btn_actions));
385 	if (offset >=0)
386 	{
387 		*handler = &priv->btn_actions[offset];
388 		*action  = &priv->keys[offset];
389 		return TRUE;
390 	}
391 
392 	offset = wcmFindProp(property, priv->wheel_actions, ARRAY_SIZE(priv->wheel_actions));
393 	if (offset >= 0)
394 	{
395 		*handler = &priv->wheel_actions[offset];
396 		*action  = &priv->wheel_keys[offset];
397 		return TRUE;
398 	}
399 
400 	offset = wcmFindProp(property, priv->strip_actions, ARRAY_SIZE(priv->strip_actions));
401 	if (offset >= 0)
402 	{
403 		*handler = &priv->strip_actions[offset];
404 		*action  = &priv->strip_keys[offset];
405 		return TRUE;
406 	}
407 
408 	return FALSE;
409 }
410 
wcmCheckActionProperty(WacomDevicePtr priv,Atom property,XIPropertyValuePtr prop)411 static int wcmCheckActionProperty(WacomDevicePtr priv, Atom property, XIPropertyValuePtr prop)
412 {
413 	CARD32 *data;
414 	int j;
415 
416 	if (!property) {
417 		DBG(3, priv, "ERROR: Atom is NONE\n");
418 		return BadMatch;
419 	}
420 
421 	if (prop == NULL) {
422 		DBG(3, priv, "ERROR: Value is NULL\n");
423 		return BadMatch;
424 	}
425 
426 	if (prop->size >= 255) {
427 		DBG(3, priv, "ERROR: Too many values (%ld > 255)\n", prop->size);
428 		return BadMatch;
429 	}
430 
431 	if (prop->format != 32) {
432 		DBG(3, priv, "ERROR: Incorrect value format (%d != 32)\n", prop->format);
433 		return BadMatch;
434 	}
435 
436 	if (prop->type != XA_INTEGER) {
437 		DBG(3, priv, "ERROR: Incorrect value type (%d != XA_INTEGER)\n", prop->type);
438 		return BadMatch;
439 	}
440 
441 	data = (CARD32*)prop->data;
442 
443 	for (j = 0; j < prop->size; j++)
444 	{
445 		int code = data[j] & AC_CODE;
446 		int type = data[j] & AC_TYPE;
447 
448 		DBG(10, priv, "Index %d == %d (type: %d, code: %d)\n", j, data[j], type, code);
449 
450 		switch(type)
451 		{
452 			case AC_KEY:
453 				break;
454 			case AC_BUTTON:
455 				if (code > WCM_MAX_X11BUTTON) {
456 					DBG(3, priv, "ERROR: AC_BUTTON code too high (%d > %d)\n", code, WCM_MAX_X11BUTTON);
457 					return BadValue;
458 				}
459 				break;
460 			case AC_MODETOGGLE:
461 				break;
462 			case AC_PANSCROLL:
463 				break;
464 			default:
465 				DBG(3, priv, "ERROR: Unknown command\n");
466 				return BadValue;
467 		}
468 	}
469 
470 	return Success;
471 }
472 
473 /**
474  * An 'Action' property (such as an element of "Wacom Button Actions")
475  * defines an action to be performed for some event. The property is
476  * validated, and then saved for later use. Both the property itself
477  * (as 'handler') and the data it references (as 'action') are saved.
478  *
479  * @param dev        The device being modified
480  * @param property   The Action property being set
481  * @param prop       The data contained in 'property'
482  * @param checkonly  'true' if the property should only be checked for validity
483  * @param handler    Pointer to the handler that must be updated
484  * @param action     Pointer to the action list that must be updated
485  */
wcmSetActionProperty(DeviceIntPtr dev,Atom property,XIPropertyValuePtr prop,BOOL checkonly,Atom * handler,unsigned int (* action)[256])486 static int wcmSetActionProperty(DeviceIntPtr dev, Atom property,
487 				XIPropertyValuePtr prop, BOOL checkonly,
488 				Atom *handler, unsigned int (*action)[256])
489 {
490 	InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
491 	WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
492 	int rc, i;
493 
494 	DBG(5, priv, "%s new actions for Atom %d\n", checkonly ? "Checking" : "Setting", property);
495 
496 	rc = wcmCheckActionProperty(priv, property, prop);
497 	if (rc != Success) {
498 		const char *msg = NULL;
499 		switch (rc) {
500 			case BadMatch: msg = "BadMatch"; break;
501 			case BadValue: msg = "BadValue"; break;
502 			default: msg = "UNKNOWN"; break;
503 		}
504 		DBG(3, priv, "Action validation failed with code %d (%s)\n", rc, msg);
505 		return rc;
506 	}
507 
508 	if (!checkonly)
509 	{
510 		memset(action, 0, sizeof(*action));
511 		for (i = 0; i < prop->size; i++)
512 			(*action)[i] = ((unsigned int*)prop->data)[i];
513 		*handler = property;
514 	}
515 
516 	return Success;
517 }
518 
wcmCheckActionsProperty(DeviceIntPtr dev,Atom property,XIPropertyValuePtr prop)519 static int wcmCheckActionsProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop)
520 {
521 	InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
522 	XIPropertyValuePtr val;
523 	Atom *values = (Atom*)prop->data;
524 	int i;
525 
526 	if (prop->format != 32 || prop->type != XA_ATOM)
527 		return BadMatch;
528 
529 	for (i = 0; i < prop->size; i++)
530 	{
531 		if (!values[i])
532 			continue;
533 
534 		if (values[i] == property || !ValidAtom(values[i]))
535 			return BadValue;
536 
537 		if (XIGetDeviceProperty(pInfo->dev, values[i], &val) != Success)
538 			return BadValue;
539 	}
540 
541 	return Success;
542 }
543 
544 /**
545  * An 'Actions' property (such as "Wacom Button Actions") stores a list of
546  * 'Action' properties that define an action to be performed. This function
547  * goes through the list of actions and saves each one.
548  *
549  * @param dev        The device being modified
550  * @param property   The Actions property being set
551  * @param prop       The data contained in 'property'
552  * @param checkonly  'true' if the property should only be checked for validity
553  * @param size       Expected number of elements in 'prop'
554  * @param handlers   List of handlers that must be updated
555  * @param actions    List of actions that must be updated
556  */
wcmSetActionsProperty(DeviceIntPtr dev,Atom property,XIPropertyValuePtr prop,BOOL checkonly,int size,Atom * handlers,unsigned int (* actions)[256])557 static int wcmSetActionsProperty(DeviceIntPtr dev, Atom property,
558                                  XIPropertyValuePtr prop, BOOL checkonly,
559                                  int size, Atom* handlers, unsigned int (*actions)[256])
560 {
561 	InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
562 	WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
563 	int rc, i;
564 
565 	DBG(10, priv, "\n");
566 
567 	if (prop->size != size)
568 		return BadValue;
569 
570 	rc = wcmCheckActionsProperty(dev, property, prop);
571 	if (rc != Success)
572 		return rc;
573 
574 	for (i = 0; i < prop->size; i++)
575 	{
576 		int index = i;
577 		Atom subproperty = ((Atom*)prop->data)[i];
578 		XIPropertyValuePtr subprop;
579 
580 		if (property == prop_btnactions)
581 		{ /* Driver uses physical -- not X11 -- button numbering internally */
582 			if (i < 3)
583 				index = i;
584 			else if (i < 7)
585 				continue;
586 			else
587 				index = i - 4;
588 		}
589 
590 		if (subproperty == 0)
591 		{ /* Interpret 'None' as meaning 'reset' */
592 			if (!checkonly)
593 			{
594 				if (property == prop_btnactions)
595 					wcmResetButtonAction(pInfo, index, size);
596 				else if (property == prop_strip_buttons)
597 					wcmResetStripAction(pInfo, index);
598 				else if (property == prop_wheel_buttons)
599 					wcmResetWheelAction(pInfo, index);
600 
601 				if (subproperty != handlers[index])
602 					subproperty = handlers[index];
603 			}
604 		}
605 		else
606 		{
607 			XIGetDeviceProperty(dev, subproperty, &subprop);
608 			rc = wcmSetActionProperty(dev, subproperty, subprop, checkonly, &handlers[index], &actions[index]);
609 			if (rc != Success)
610 				return rc;
611 		}
612 	}
613 
614 	return Success;
615 }
616 
617 /**
618  * Update the rotation property for all tools on the same physical tablet as
619  * pInfo.
620  */
wcmUpdateRotationProperty(WacomDevicePtr priv)621 void wcmUpdateRotationProperty(WacomDevicePtr priv)
622 {
623 	WacomCommonPtr common = priv->common;
624 	WacomDevicePtr other;
625 	char rotation = common->wcmRotate;
626 
627 	for (other = common->wcmDevices; other; other = other->next)
628 	{
629 		InputInfoPtr pInfo;
630 		DeviceIntPtr dev;
631 
632 		if (other == priv)
633 			continue;
634 
635 		pInfo = other->pInfo;
636 		dev = pInfo->dev;
637 
638 		XIChangeDeviceProperty(dev, prop_rotation, XA_INTEGER, 8,
639 				       PropModeReplace, 1, &rotation,
640 				       TRUE);
641 	}
642 }
643 
644 static void
wcmSetHWTouchProperty(InputInfoPtr pInfo)645 wcmSetHWTouchProperty(InputInfoPtr pInfo)
646 {
647 	WacomDevicePtr priv = pInfo->private;
648 	WacomCommonPtr common = priv->common;
649 	XIPropertyValuePtr prop;
650 	CARD8 prop_value;
651 	int rc;
652 
653 	rc = XIGetDeviceProperty(pInfo->dev, prop_hardware_touch, &prop);
654 	if (rc != Success || prop->format != 8 || prop->size != 1)
655 	{
656 		xf86Msg(X_ERROR, "%s: Failed to update hardware touch state.\n",
657 			pInfo->name);
658 		return;
659 	}
660 
661 	prop_value = common->wcmHWTouchSwitchState;
662 	XIChangeDeviceProperty(pInfo->dev, prop_hardware_touch, XA_INTEGER,
663 			       prop->format, PropModeReplace,
664 			       prop->size, &prop_value, TRUE);
665 }
666 
667 static CARD32
touchTimerFunc(OsTimerPtr timer,CARD32 now,pointer arg)668 touchTimerFunc(OsTimerPtr timer, CARD32 now, pointer arg)
669 {
670 	InputInfoPtr pInfo = arg;
671 #if !HAVE_THREADED_INPUT
672 	int sigstate = xf86BlockSIGIO();
673 #endif
674 
675 	wcmSetHWTouchProperty(pInfo);
676 
677 #if !HAVE_THREADED_INPUT
678 	xf86UnblockSIGIO(sigstate);
679 #endif
680 
681 	return 0;
682 }
683 
684 /**
685  * Update HW touch property when its state is changed by touch switch
686  */
687 void
wcmUpdateHWTouchProperty(WacomDevicePtr priv,int hw_touch)688 wcmUpdateHWTouchProperty(WacomDevicePtr priv, int hw_touch)
689 {
690 	WacomCommonPtr common = priv->common;
691 
692 	if (hw_touch == common->wcmHWTouchSwitchState)
693 		return;
694 
695 	common->wcmHWTouchSwitchState = hw_touch;
696 
697 	/* This function is called during SIGIO/InputThread. Schedule timer
698 	 * for property event delivery by the main thread. */
699 	priv->touch_timer = TimerSet(priv->touch_timer, 0 /* reltime */,
700 				      1, touchTimerFunc, priv->pInfo);
701 }
702 
703 /**
704  * Only allow deletion of a property if it is not being used by any of the
705  * button actions.
706  */
wcmDeleteProperty(DeviceIntPtr dev,Atom property)707 int wcmDeleteProperty(DeviceIntPtr dev, Atom property)
708 {
709 	InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
710 	WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
711 	int i;
712 
713 	i = wcmFindProp(property, priv->btn_actions, ARRAY_SIZE(priv->btn_actions));
714 	if (i < 0)
715 		i = wcmFindProp(property, priv->wheel_actions,
716 				ARRAY_SIZE(priv->wheel_actions));
717 	if (i < 0)
718 		i = wcmFindProp(property, priv->strip_actions,
719 				ARRAY_SIZE(priv->strip_actions));
720 
721 	return (i >= 0) ? BadAccess : Success;
722 }
723 
wcmSetProperty(DeviceIntPtr dev,Atom property,XIPropertyValuePtr prop,BOOL checkonly)724 int wcmSetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
725 		BOOL checkonly)
726 {
727 	InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
728 	WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
729 	WacomCommonPtr common = priv->common;
730 
731 	DBG(10, priv, "\n");
732 
733 	if (property == prop_devnode || property == prop_product_id)
734 		return BadValue; /* Read-only */
735 	else if (property == prop_tablet_area)
736 	{
737 		INT32 *values = (INT32*)prop->data;
738 
739 		if (prop->size != 4 || prop->format != 32)
740 			return BadValue;
741 
742 		if (!checkonly)
743 		{
744 			if ((values[0] == -1) && (values[1] == -1) &&
745 					(values[2] == -1) && (values[3] == -1))
746 			{
747 				values[0] = priv->minX;
748 				values[1] = priv->minX;
749 				values[2] = priv->maxX;
750 				values[3] = priv->maxY;
751 			}
752 
753 			priv->topX = values[0];
754 			priv->topY = values[1];
755 			priv->bottomX = values[2];
756 			priv->bottomY = values[3];
757 		}
758 	} else if (property == prop_pressurecurve)
759 	{
760 		INT32 *pcurve;
761 
762 		if (prop->size != 4 || prop->format != 32)
763 			return BadValue;
764 
765 		pcurve = (INT32*)prop->data;
766 
767 		if (!wcmCheckPressureCurveValues(pcurve[0], pcurve[1],
768 						 pcurve[2], pcurve[3]))
769 			return BadValue;
770 
771 		if (IsCursor(priv) || IsPad (priv))
772 			return BadValue;
773 
774 		if (!checkonly)
775 			wcmSetPressureCurve (priv, pcurve[0], pcurve[1],
776 					pcurve[2], pcurve[3]);
777 	} else if (property == prop_suppress)
778 	{
779 		CARD32 *values;
780 
781 		if (prop->size != 2 || prop->format != 32)
782 			return BadValue;
783 
784 		values = (CARD32*)prop->data;
785 
786 		if (values[0] > 100)
787 			return BadValue;
788 
789 		if ((values[1] < 1) || (values[1] > MAX_SAMPLES))
790 			return BadValue;
791 
792 		if (!checkonly)
793 		{
794 			common->wcmSuppress = values[0];
795 			common->wcmRawSample = values[1];
796 		}
797 	} else if (property == prop_rotation)
798 	{
799 		CARD8 value;
800 		if (prop->size != 1 || prop->format != 8)
801 			return BadValue;
802 
803 		value = *(CARD8*)prop->data;
804 
805 		if (value > 3)
806 			return BadValue;
807 
808 		if (!checkonly && common->wcmRotate != value)
809 			wcmRotateTablet(pInfo, value);
810 
811 	} else if (property == prop_serials)
812 	{
813 		/* This property is read-only but we need to
814 		 * set it at runtime. If we get here from wcmUpdateSerial,
815 		 * we know the serial has ben set internally already, so we
816 		 * can reply with success. */
817 		if (prop->size == 5 && prop->format == 32)
818 			if (((CARD32*)prop->data)[3] == priv->cur_serial)
819 				return Success;
820 
821 		return BadValue; /* Read-only */
822 	} else if (property == prop_serial_binding)
823 	{
824 		unsigned int serial;
825 
826 		if (prop->size != 1 || prop->format != 32)
827 			return BadValue;
828 
829 		if (!checkonly)
830 		{
831 			serial = *(CARD32*)prop->data;
832 			wcmBindToSerial(pInfo, serial);
833 		}
834 	} else if (property == prop_strip_buttons)
835 		return wcmSetActionsProperty(dev, property, prop, checkonly, ARRAY_SIZE(priv->strip_actions), priv->strip_actions, priv->strip_keys);
836 	else if (property == prop_wheel_buttons)
837 		return wcmSetActionsProperty(dev, property, prop, checkonly, ARRAY_SIZE(priv->wheel_actions), priv->wheel_actions, priv->wheel_keys);
838 	else if (property == prop_proxout)
839 	{
840 		CARD32 value;
841 
842 		if (prop->size != 1 || prop->format != 32)
843 			return BadValue;
844 
845 		if (!IsTablet (priv))
846 			return BadValue;
847 
848 		value = *(CARD32*)prop->data;
849 
850 		if (value > common->wcmMaxDist)
851 			return BadValue;
852 
853 		if (!checkonly)
854 			priv->wcmProxoutDist = value;
855 	} else if (property == prop_threshold)
856 	{
857 		const INT32 MAXIMUM = wcmInternalToUserPressure(pInfo, priv->maxCurve);
858 		INT32 value;
859 
860 		if (prop->size != 1 || prop->format != 32)
861 			return BadValue;
862 
863 		value = *(INT32*)prop->data;
864 
865 		if (value == -1)
866 			value = priv->maxCurve * DEFAULT_THRESHOLD;
867 		else if ((value < 1) || (value > MAXIMUM))
868 			return BadValue;
869 		else
870 			value = wcmUserToInternalPressure(pInfo, value);
871 
872 		if (!checkonly)
873 			common->wcmThreshold = value;
874 	} else if (property == prop_touch)
875 	{
876 		CARD8 *values = (CARD8*)prop->data;
877 
878 		if (prop->size != 1 || prop->format != 8)
879 			return BadValue;
880 
881 		if ((values[0] != 0) && (values[0] != 1))
882 			return BadValue;
883 
884 		if (!checkonly && common->wcmTouch != values[0])
885 			common->wcmTouch = values[0];
886 	} else if (property == prop_hardware_touch)
887 	{
888 		if (common->wcmHasHWTouchSwitch)
889 		{
890 			/* If we get here from wcmUpdateHWTouchProperty, we know
891 			 * the wcmHWTouchSwitchState has been set internally
892 			 * already, so we can reply with success. */
893 			if (prop->size == 1 && prop->format == 8)
894 				if (((CARD8*)prop->data)[0] == common->wcmHWTouchSwitchState)
895 					return Success;
896 		}
897 
898 		return BadValue; /* read-only */
899 	} else if (property == prop_gesture)
900 	{
901 		CARD8 *values = (CARD8*)prop->data;
902 
903 		if (prop->size != 1 || prop->format != 8)
904 			return BadValue;
905 
906 		if ((values[0] != 0) && (values[0] != 1))
907 			return BadValue;
908 
909 		if (!checkonly && common->wcmGesture != values[0])
910 			common->wcmGesture = values[0];
911 	} else if (property == prop_gesture_param)
912 	{
913 		CARD32 *values;
914 
915 		if (prop->size != 3 || prop->format != 32)
916 			return BadValue;
917 
918 		values = (CARD32*)prop->data;
919 
920 		if (!checkonly)
921 		{
922 			if (common->wcmGestureParameters.wcmZoomDistance != values[0])
923 				common->wcmGestureParameters.wcmZoomDistance = values[0];
924 			if (common->wcmGestureParameters.wcmScrollDistance != values[1])
925 				common->wcmGestureParameters.wcmScrollDistance = values[1];
926 			if (common->wcmGestureParameters.wcmTapTime != values[2])
927 				common->wcmGestureParameters.wcmTapTime = values[2];
928 		}
929 	} else if (property == prop_hover)
930 	{
931 		CARD8 *values = (CARD8*)prop->data;
932 
933 		if (prop->size != 1 || prop->format != 8)
934 			return BadValue;
935 
936 		if ((values[0] != 0) && (values[0] != 1))
937 			return BadValue;
938 
939 		if (!IsStylus(priv))
940 			return BadMatch;
941 
942 		if (!checkonly)
943 			common->wcmTPCButton = !values[0];
944 #ifdef DEBUG
945 	} else if (property == prop_debuglevels)
946 	{
947 		CARD8 *values;
948 
949 		if (prop->size != 2 || prop->format != 8)
950 			return BadMatch;
951 
952 		values = (CARD8*)prop->data;
953 		if (values[0] > 12 || values[1] > 12)
954 			return BadValue;
955 
956 		if (!checkonly)
957 		{
958 			priv->debugLevel = values[0];
959 			common->debugLevel = values[1];
960 		}
961 #endif
962 	} else if (property == prop_btnactions)
963 	{
964 		int nbuttons = priv->nbuttons < 4 ? priv->nbuttons : priv->nbuttons + 4;
965 		return wcmSetActionsProperty(dev, property, prop, checkonly, nbuttons, priv->btn_actions, priv->keys);
966 	} else if (property == prop_pressure_recal)
967 	{
968 		CARD8 *values = (CARD8*)prop->data;
969 
970 		if (prop->size != 1 || prop->format != 8)
971 			return BadValue;
972 
973 		if ((values[0] != 0) && (values[0] != 1))
974 			return BadValue;
975 
976 		if (!IsStylus(priv) && !IsEraser(priv))
977 			return BadMatch;
978 
979 		if (!checkonly)
980 			common->wcmPressureRecalibration = values[0];
981 	} else if (property == prop_panscroll_threshold)
982 	{
983 		CARD32 *values = (CARD32*)prop->data;
984 
985 		if (prop->size != 1 || prop->format != 32)
986 			return BadValue;
987 
988 		if (values[0] <= 0)
989 			return BadValue;
990 
991 		if (IsTouch(priv))
992 			return BadMatch;
993 
994 		if (!checkonly)
995 			common->wcmPanscrollThreshold = values[0];
996 	} else
997 	{
998 		Atom *handler = NULL;
999 		unsigned int (*action)[256] = NULL;
1000 		if (wcmFindActionHandler(priv, property, &handler, &action))
1001 			return wcmSetActionProperty(dev, property, prop, checkonly, handler, action);
1002 		/* backwards-compatible behavior silently ignores the not-found case */
1003 	}
1004 
1005 	return Success;
1006 }
1007 
wcmGetProperty(DeviceIntPtr dev,Atom property)1008 int wcmGetProperty (DeviceIntPtr dev, Atom property)
1009 {
1010 	InputInfoPtr pInfo = (InputInfoPtr) dev->public.devicePrivate;
1011 	WacomDevicePtr priv = (WacomDevicePtr) pInfo->private;
1012 	WacomCommonPtr common = priv->common;
1013 
1014 	DBG(10, priv, "\n");
1015 
1016 	if (property == prop_serials)
1017 	{
1018 		uint32_t values[5];
1019 
1020 		values[0] = common->tablet_id;
1021 		values[1] = priv->oldState.serial_num;
1022 		values[2] = priv->oldState.device_id;
1023 		values[3] = priv->cur_serial;
1024 		values[4] = priv->cur_device_id;
1025 
1026 		DBG(10, priv, "Update to serial: %d\n", priv->oldState.serial_num);
1027 
1028 		return XIChangeDeviceProperty(dev, property, XA_INTEGER, 32,
1029 					      PropModeReplace, 5,
1030 					      values, FALSE);
1031 	}
1032 	else if (property == prop_btnactions)
1033 	{
1034 		/* Convert the physical button representation used internally
1035 		 * to the X11 button representation we've historically used.
1036 		 * To do this, we need to skip X11 buttons 4-7 which would be
1037 		 * used by a scroll wheel rather than an actual button.
1038 		 */
1039 		int nbuttons = priv->nbuttons < 4 ? priv->nbuttons : priv->nbuttons + 4;
1040 		Atom x11_btn_actions[nbuttons];
1041 		int i;
1042 
1043 		for (i = 0; i < nbuttons; i++)
1044 		{
1045 			if (i < 3)
1046 				x11_btn_actions[i] = priv->btn_actions[i];
1047 			else if (i < 7)
1048 				x11_btn_actions[i] = 0;
1049 			else
1050 				x11_btn_actions[i] = priv->btn_actions[i-4];
1051 		}
1052 
1053 		return XIChangeDeviceProperty(dev, property, XA_ATOM, 32,
1054 		                              PropModeReplace, nbuttons,
1055 		                              x11_btn_actions, FALSE);
1056 	}
1057 	else if (property == prop_strip_buttons)
1058 	{
1059 		return XIChangeDeviceProperty(dev, property, XA_ATOM, 32,
1060 					      PropModeReplace, ARRAY_SIZE(priv->strip_actions),
1061 					      priv->strip_actions, FALSE);
1062 	}
1063 	else if (property == prop_wheel_buttons)
1064 	{
1065 		return XIChangeDeviceProperty(dev, property, XA_ATOM, 32,
1066 		                              PropModeReplace, ARRAY_SIZE(priv->wheel_actions),
1067 		                              priv->wheel_actions, FALSE);
1068 	}
1069 
1070 	return Success;
1071 }
1072 
1073 static void
wcmSetSerialProperty(InputInfoPtr pInfo)1074 wcmSetSerialProperty(InputInfoPtr pInfo)
1075 {
1076 	WacomDevicePtr priv = pInfo->private;
1077 	XIPropertyValuePtr prop;
1078 	CARD32 prop_value[5];
1079 	int rc;
1080 
1081 	rc = XIGetDeviceProperty(pInfo->dev, prop_serials, &prop);
1082 	if (rc != Success || prop->format != 32 || prop->size != 5)
1083 	{
1084 		xf86Msg(X_ERROR, "%s: Failed to update serial number.\n",
1085 			pInfo->name);
1086 		return;
1087 	}
1088 
1089 	memcpy(prop_value, prop->data, sizeof(prop_value));
1090 	prop_value[3] = priv->cur_serial;
1091 	prop_value[4] = priv->cur_device_id;
1092 
1093 	XIChangeDeviceProperty(pInfo->dev, prop_serials, XA_INTEGER,
1094 			       prop->format, PropModeReplace,
1095 			       prop->size, prop_value, TRUE);
1096 }
1097 
1098 static CARD32
serialTimerFunc(OsTimerPtr timer,CARD32 now,pointer arg)1099 serialTimerFunc(OsTimerPtr timer, CARD32 now, pointer arg)
1100 {
1101 	InputInfoPtr pInfo = arg;
1102 
1103 #if !HAVE_THREADED_INPUT
1104 	int sigstate = xf86BlockSIGIO();
1105 #endif
1106 
1107 	wcmSetSerialProperty(pInfo);
1108 
1109 #if !HAVE_THREADED_INPUT
1110 	xf86UnblockSIGIO(sigstate);
1111 #endif
1112 
1113 	return 0;
1114 }
1115 
1116 void
wcmUpdateSerial(InputInfoPtr pInfo,unsigned int serial,int id)1117 wcmUpdateSerial(InputInfoPtr pInfo, unsigned int serial, int id)
1118 {
1119 	WacomDevicePtr priv = pInfo->private;
1120 
1121 	if (priv->cur_serial == serial && priv->cur_device_id == id)
1122 		return;
1123 
1124 	priv->cur_serial = serial;
1125 	priv->cur_device_id = id;
1126 
1127 	/* This function is called during SIGIO/InputThread. Schedule timer
1128 	 * for property event delivery by the main thread. */
1129 	priv->serial_timer = TimerSet(priv->serial_timer, 0 /* reltime */,
1130 				      1, serialTimerFunc, pInfo);
1131 }
1132 
1133 static void
wcmBindToSerial(InputInfoPtr pInfo,unsigned int serial)1134 wcmBindToSerial(InputInfoPtr pInfo, unsigned int serial)
1135 {
1136 	WacomDevicePtr priv = pInfo->private;
1137 
1138 	priv->serial = serial;
1139 
1140 }
1141 
1142 /* vim: set noexpandtab tabstop=8 shiftwidth=8: */
1143