Re: [PATCH v10 2/4] Input: touch-overlay - Add touchscreen overlay handling

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 09/07/2024 02:08, Dmitry Torokhov wrote:
> Hi Javier,
> 
> On Wed, Jun 26, 2024 at 11:56:14AM +0200, Javier Carrasco wrote:
>> Some touch devices provide mechanical overlays with different objects
>> like buttons or clipped touchscreen surfaces.
> 
> Thank you for your work. I think it is pretty much ready to be merged,
> just a few small comments:
> 
>>
>> In order to support these objects, add a series of helper functions
>> to the input subsystem to transform them into overlay objects via
>> device tree nodes.
>>
>> These overlay objects consume the raw touch events and report the
>> expected input events depending on the object properties.
> 
> So if we have overlays and also want to invert/swap axis then the
> overlays should be processed first and only then
> touchscreen_set_mt_pos() or touchscreen_report_pos() should be called?
> 
> But then it will not work if we need help frm the input core to assign
> slots in cases when touch controller does not implement [reliable]
> contact tracing/identification.
> 
> I think this all needs to be clarified.
> 

I think this is the most critical point from your feedback.

So far, touch-overlay processes the coordinates of input_mt_pos elements
before passing them to touchscreen_set_mt_pos(). As you pointed out,
that means that it does not act on the slots i.e. it assumes that the
controller does the contact tracing and pos[i] is assigned to slot[i],
which is wrong.

Current status:
[Sensor]->[touch-overlay(filter + button
events)]->[touchscreen_set_mt_pos()]->[input_mt_assign_slots()]->[report
MT events]

If I am not mistaken, I would need a solution that processes the
coordinates associated to assigned slots via input_mt_assign_slots().
Then I would have to avoid generating MT events from slots whose
coordinates are outside of the overlay frame (ignored) or on overlay
buttons (generating button press/release events instead).

But once input_mt_assign_slots() is called, it is already too late for
that processing, isn't it? Unless assigned slots filtered by
touch-overlay are kept from generating MT events somehow, but still used
to keep contact tracing.

Any suggestion on this respect would be more than welcome.

>>
>> Note that the current implementation allows for multiple definitions
>> of touchscreen areas (regions that report touch events), but only the
>> first one will be used for the touchscreen device that the consumers
>> typically provide.
>> Should the need for multiple touchscreen areas arise, additional
>> touchscreen devices would be required at the consumer side.
>> There is no limitation in the number of touch areas defined as buttons.
>>
>> Reviewed-by: Jeff LaBundy <jeff@xxxxxxxxxxx>
>> Signed-off-by: Javier Carrasco <javier.carrasco@xxxxxxxxxxxxxx>
> 
>> +int touch_overlay_map(struct list_head *list, struct input_dev *input)
>> +{
>> +	struct fwnode_handle *overlay, *fw_segment;
>> +	struct device *dev = input->dev.parent;
>> +	struct touch_overlay_segment *segment;
>> +	int error = 0;
>> +
>> +	overlay = device_get_named_child_node(dev, "touch-overlay");
> 
> We can annotate this as
> 
> 	struct fwnode_handle *overlay __free(fwnode_handle) = 
> 		device_get_named_child_node(dev, "touch-overlay");
> 

That's right. A scoped version of the loop would have been nice too, but
there is still no such variant for this particular one. I am pushing
that somewhere else, though.

>> +	if (!overlay)
>> +		return 0;
>> +
>> +	fwnode_for_each_available_child_node(overlay, fw_segment) {
>> +		segment = devm_kzalloc(dev, sizeof(*segment), GFP_KERNEL);
>> +		if (!segment) {
>> +			fwnode_handle_put(fw_segment);
>> +			error = -ENOMEM;
> 
> return -ENOMEM;
> 
>> +			break;
>> +		}
>> +		error = touch_overlay_get_segment(fw_segment, segment, input);
>> +		if (error) {
>> +			fwnode_handle_put(fw_segment);
> 
> return error;
> 
>> +			break;
>> +		}
>> +		list_add_tail(&segment->list, list);
>> +	}
>> +	fwnode_handle_put(overlay);
> 
> Drop.
> 
>> +
>> +	return error;
> 
> return 0;
> 

Ack.

>> +}
>> +EXPORT_SYMBOL(touch_overlay_map);
>> +
>> +/**
>> + * touch_overlay_get_touchscreen_abs - get abs size from the touchscreen area.
>> + * @list: pointer to the list that holds the segments
>> + * @x: horizontal abs
>> + * @y: vertical abs
>> + */
>> +void touch_overlay_get_touchscreen_abs(struct list_head *list, u16 *x, u16 *y)
>> +{
>> +	struct touch_overlay_segment *segment;
>> +	struct list_head *ptr;
>> +
>> +	list_for_each(ptr, list) {
>> +		segment = list_entry(ptr, struct touch_overlay_segment, list);
>> +		if (!segment->key) {
>> +			*x = segment->x_size - 1;
>> +			*y = segment->y_size - 1;
>> +			break;
>> +		}
>> +	}
>> +}
>> +EXPORT_SYMBOL(touch_overlay_get_touchscreen_abs);
>> +
>> +static bool touch_overlay_segment_event(struct touch_overlay_segment *seg,
>> +					u32 x, u32 y)
>> +{
>> +	if (!seg)
>> +		return false;
> 
> This is a static function in the module, we are not passing NULL
> segments to it ever. Such tests should be done on API boundary.
> 

Ack.

>> +
>> +	if (x >= seg->x_origin && x < (seg->x_origin + seg->x_size) &&
>> +	    y >= seg->y_origin && y < (seg->y_origin + seg->y_size))
>> +		return true;
>> +
>> +	return false;
>> +}
>> +
>> +/**
>> + * touch_overlay_mapped_touchscreen - check if a touchscreen area is mapped
>> + * @list: pointer to the list that holds the segments
>> + *
>> + * Returns true if a touchscreen area is mapped or false otherwise.
>> + */
>> +bool touch_overlay_mapped_touchscreen(struct list_head *list)
>> +{
>> +	struct touch_overlay_segment *segment;
>> +	struct list_head *ptr;
>> +
>> +	list_for_each(ptr, list) {
>> +		segment = list_entry(ptr, struct touch_overlay_segment, list);
>> +		if (!segment->key)
>> +			return true;
>> +	}
>> +
>> +	return false;
>> +}
>> +EXPORT_SYMBOL(touch_overlay_mapped_touchscreen);
>> +
>> +static bool touch_overlay_event_on_ts(struct list_head *list, u32 *x, u32 *y)
>> +{
>> +	struct touch_overlay_segment *segment;
>> +	struct list_head *ptr;
>> +	bool valid_touch = true;
>> +
>> +	if (!x || !y)
>> +		return false;
>> +
>> +	list_for_each(ptr, list) {
>> +		segment = list_entry(ptr, struct touch_overlay_segment, list);
>> +		if (segment->key)
>> +			continue;
>> +
>> +		if (touch_overlay_segment_event(segment, *x, *y)) {
>> +			*x -= segment->x_origin;
>> +			*y -= segment->y_origin;
>> +			return true;
>> +		}
>> +		/* ignore touch events outside the defined area */
>> +		valid_touch = false;
>> +	}
>> +
>> +	return valid_touch;
>> +}
>> +
>> +static bool touch_overlay_button_event(struct input_dev *input,
>> +				       struct touch_overlay_segment *segment,
>> +				       const u32 *x, const u32 *y, u32 slot)
>> +{
>> +	bool contact = x && y;
>> +
>> +	if (segment->slot == slot && segment->pressed) {
>> +		/* button release */
>> +		if (!contact) {
>> +			segment->pressed = false;
>> +			input_report_key(input, segment->key, false);
>> +			input_sync(input);
> 
> Do we really need to emit sync here? Can we require/rely on the driver
> calling us to emit input_sync() once it's done processing current
> frame/packet?
>

I will test it without it, but that might change anyway depending on the
outcome of your first comment.

>> +			return true;
>> +		}
>> +
>> +		/* sliding out of the button releases it */
>> +		if (!touch_overlay_segment_event(segment, *x, *y)) {
>> +			segment->pressed = false;
>> +			input_report_key(input, segment->key, false);
>> +			input_sync(input);
>> +			/* keep available for a possible touch event */
>> +			return false;
>> +		}
>> +		/* ignore sliding on the button while pressed */
>> +		return true;
>> +	} else if (contact && touch_overlay_segment_event(segment, *x, *y)) {
>> +		segment->pressed = true;
>> +		segment->slot = slot;
>> +		input_report_key(input, segment->key, true);
>> +		input_sync(input);
>> +		return true;
>> +	}
>> +
>> +	return false;
>> +}
>> +
>> +/**
>> + * touch_overlay_process_event - process input events according to the overlay
>> + * mapping. This function acts as a filter to release the calling driver from
>> + * the events that are either related to overlay buttons or out of the overlay
>> + * touchscreen area, if defined.
>> + * @list: pointer to the list that holds the segments
>> + * @input: pointer to the input device associated to the event
>> + * @x: pointer to the x coordinate (NULL if not available - no contact)
>> + * @y: pointer to the y coordinate (NULL if not available - no contact)
> 
> Would it be better to have a separate argument communicating slot state
> (contact/no contact)?
>

Passing NULL would be ok to save one extra argument, but I have no
strong feelings about it.

>> + * @slot: slot associated to the event
> 
> What if we are not dealing with an MT device? Can we say that they
> should use slot 0 or maybe -1?
>

slot 0 would be ok. I will document it.

>> + *
>> + * Returns true if the event was processed (reported for valid key events
>> + * and dropped for events outside the overlay touchscreen area) or false
>> + * if the event must be processed by the caller. In that case this function
>> + * shifts the (x,y) coordinates to the overlay touchscreen axis if required.
>> + */
>> +bool touch_overlay_process_event(struct list_head *list,
>> +				 struct input_dev *input,
>> +				 u32 *x, u32 *y, u32 slot)
>> +{
>> +	struct touch_overlay_segment *segment;
>> +	struct list_head *ptr;
>> +
>> +	/*
>> +	 * buttons must be prioritized over overlay touchscreens to account for
>> +	 * overlappings e.g. a button inside the touchscreen area.
>> +	 */
>> +	list_for_each(ptr, list) {
>> +		segment = list_entry(ptr, struct touch_overlay_segment, list);
>> +		if (segment->key &&
>> +		    touch_overlay_button_event(input, segment, x, y, slot))
>> +			return true;
>> +	}
>> +
>> +	/*
>> +	 * valid touch events on the overlay touchscreen are left for the client
>> +	 * to be processed/reported according to its (possibly) unique features.
>> +	 */
>> +	return !touch_overlay_event_on_ts(list, x, y);
>> +}
>> +EXPORT_SYMBOL(touch_overlay_process_event);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("Helper functions for overlay objects on touch devices");
>> diff --git a/include/linux/input/touch-overlay.h b/include/linux/input/touch-overlay.h
>> new file mode 100644
>> index 000000000000..cef05c46000d
>> --- /dev/null
>> +++ b/include/linux/input/touch-overlay.h
>> @@ -0,0 +1,22 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (c) 2023 Javier Carrasco <javier.carrasco@xxxxxxxxxxxxxx>
>> + */
>> +
>> +#ifndef _TOUCH_OVERLAY
>> +#define _TOUCH_OVERLAY
>> +
>> +#include <linux/types.h>
>> +
>> +struct input_dev;
>> +
>> +int touch_overlay_map(struct list_head *list, struct input_dev *input);
>> +
>> +void touch_overlay_get_touchscreen_abs(struct list_head *list, u16 *x, u16 *y);
>> +
>> +bool touch_overlay_mapped_touchscreen(struct list_head *list);
>> +
>> +bool touch_overlay_process_event(struct list_head *list, struct input_dev *input,
>> +				 u32 *x, u32 *y, u32 slot);
>> +
>> +#endif
>>
>> -- 
>> 2.40.1
>>
> 
> Thanks.
> 

Thanks a lot for your feedback.

Best regards,
Javier Carrasco





[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux