1 /*
2  * Copyright 2023 Fabian Blatz <fabianblatz@gmail.com>
3  * Copyright 2025 Abderrahmane JARMOUNI
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT zephyr_lvgl_pointer_input
9 
10 #include "lvgl_common_input.h"
11 #include "lvgl_pointer_input.h"
12 
13 #include <lvgl_display.h>
14 #include <zephyr/logging/log.h>
15 
16 LOG_MODULE_DECLARE(lvgl, CONFIG_LV_Z_LOG_LEVEL);
17 
18 struct lvgl_pointer_input_config {
19 	struct lvgl_common_input_config common_config; /* Needs to be first member */
20 	bool swap_xy;
21 	bool invert_x;
22 	bool invert_y;
23 };
24 
25 struct lvgl_pointer_input_data {
26 	struct lvgl_common_input_data common_data;
27 	uint32_t point_x;
28 	uint32_t point_y;
29 };
30 
lvgl_pointer_process_event(struct input_event * evt,void * user_data)31 static void lvgl_pointer_process_event(struct input_event *evt, void *user_data)
32 {
33 	const struct device *dev = user_data;
34 	const struct lvgl_pointer_input_config *cfg = dev->config;
35 	struct lvgl_pointer_input_data *data = dev->data;
36 	lv_display_t *disp = lv_indev_get_display(data->common_data.indev);
37 	struct lvgl_disp_data *disp_data = (struct lvgl_disp_data *)lv_display_get_user_data(disp);
38 	struct display_capabilities *cap;
39 	lv_point_t *point;
40 
41 	if (disp_data == NULL) {
42 		LOG_WRN_ONCE("disp_data is NULL");
43 		return;
44 	}
45 
46 	cap = &disp_data->cap;
47 	point = &data->common_data.pending_event.point;
48 
49 	switch (evt->code) {
50 	case INPUT_ABS_X:
51 		if (cfg->swap_xy) {
52 			data->point_y = evt->value;
53 		} else {
54 			data->point_x = evt->value;
55 		}
56 		break;
57 	case INPUT_ABS_Y:
58 		if (cfg->swap_xy) {
59 			data->point_x = evt->value;
60 		} else {
61 			data->point_y = evt->value;
62 		}
63 		break;
64 	case INPUT_BTN_TOUCH:
65 		data->common_data.pending_event.state =
66 			evt->value ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
67 		break;
68 	default:
69 		return;
70 	}
71 
72 	if (!evt->sync) {
73 		return;
74 	}
75 
76 	lv_point_t tmp_point = {
77 		.x = data->point_x,
78 		.y = data->point_y,
79 	};
80 
81 	if (cfg->invert_x) {
82 		if (cap->current_orientation == DISPLAY_ORIENTATION_NORMAL ||
83 		    cap->current_orientation == DISPLAY_ORIENTATION_ROTATED_180) {
84 			tmp_point.x = cap->x_resolution - tmp_point.x;
85 		} else {
86 			tmp_point.x = cap->y_resolution - tmp_point.x;
87 		}
88 	}
89 
90 	if (cfg->invert_y) {
91 		if (cap->current_orientation == DISPLAY_ORIENTATION_NORMAL ||
92 		    cap->current_orientation == DISPLAY_ORIENTATION_ROTATED_180) {
93 			tmp_point.y = cap->y_resolution - tmp_point.y;
94 		} else {
95 			tmp_point.y = cap->x_resolution - tmp_point.y;
96 		}
97 	}
98 
99 	/* rotate touch point to match display rotation */
100 	switch (cap->current_orientation) {
101 	case DISPLAY_ORIENTATION_NORMAL:
102 		point->x = tmp_point.x;
103 		point->y = tmp_point.y;
104 		break;
105 	case DISPLAY_ORIENTATION_ROTATED_90:
106 		point->x = tmp_point.y;
107 		point->y = cap->y_resolution - tmp_point.x;
108 		break;
109 	case DISPLAY_ORIENTATION_ROTATED_180:
110 		point->x = cap->x_resolution - tmp_point.x;
111 		point->y = cap->y_resolution - tmp_point.y;
112 		break;
113 	case DISPLAY_ORIENTATION_ROTATED_270:
114 		point->x = cap->x_resolution - tmp_point.y;
115 		point->y = tmp_point.x;
116 		break;
117 	default:
118 		LOG_ERR("Invalid display orientation");
119 		break;
120 	}
121 
122 	/* filter readings within display */
123 	if (point->x <= 0) {
124 		point->x = 0;
125 	} else if (point->x >= cap->x_resolution) {
126 		point->x = cap->x_resolution - 1;
127 	}
128 
129 	if (point->y <= 0) {
130 		point->y = 0;
131 	} else if (point->y >= cap->y_resolution) {
132 		point->y = cap->y_resolution - 1;
133 	}
134 
135 	if (k_msgq_put(cfg->common_config.event_msgq, &data->common_data.pending_event,
136 		       K_NO_WAIT) != 0) {
137 		LOG_WRN("Could not put input data into queue");
138 	}
139 }
140 
lvgl_pointer_input_init(const struct device * dev)141 int lvgl_pointer_input_init(const struct device *dev)
142 {
143 	return lvgl_input_register_driver(LV_INDEV_TYPE_POINTER, dev);
144 }
145 
146 #define LVGL_POINTER_INPUT_DEFINE(inst)                                                            \
147 	LVGL_INPUT_INST_DEFINE(inst, pointer, CONFIG_LV_Z_POINTER_INPUT_MSGQ_COUNT,                \
148 			       lvgl_pointer_process_event);                                        \
149 	static const struct lvgl_pointer_input_config lvgl_pointer_input_config_##inst = {         \
150 		.common_config.event_msgq = &LVGL_INPUT_EVENT_MSGQ(inst, pointer),                 \
151 		.common_config.display_dev =                                                       \
152 			DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(inst, display)),                     \
153 		.swap_xy = DT_INST_PROP(inst, swap_xy),                                            \
154 		.invert_x = DT_INST_PROP(inst, invert_x),                                          \
155 		.invert_y = DT_INST_PROP(inst, invert_y),                                          \
156 	};                                                                                         \
157 	static struct lvgl_pointer_input_data lvgl_pointer_input_data_##inst;                      \
158 	DEVICE_DT_INST_DEFINE(inst, NULL, NULL, &lvgl_pointer_input_data_##inst,                   \
159 			      &lvgl_pointer_input_config_##inst, POST_KERNEL,                      \
160 			      CONFIG_INPUT_INIT_PRIORITY, NULL);
161 
162 DT_INST_FOREACH_STATUS_OKAY(LVGL_POINTER_INPUT_DEFINE)
163 
164 #ifdef CONFIG_LV_Z_POINTER_FROM_CHOSEN_TOUCH
165 
166 #define CHOSEN_TOUCH_DEV                 DEVICE_DT_GET(DT_CHOSEN(zephyr_touch))
167 #define LVGL_CHOSEN_TOUCH_POINTER_DEV_ID pointer_chosen_zephyr_touch
168 
169 DEVICE_DECLARE(LVGL_CHOSEN_TOUCH_POINTER_DEV_ID);
170 
171 LVGL_INPUT_DEFINE(CHOSEN_TOUCH_DEV, DEVICE_GET(LVGL_CHOSEN_TOUCH_POINTER_DEV_ID),
172 		  LVGL_CHOSEN_TOUCH_POINTER_DEV_ID, pointer, CONFIG_LV_Z_POINTER_INPUT_MSGQ_COUNT,
173 		  lvgl_pointer_process_event);
174 
175 static const struct lvgl_pointer_input_config chosen_touch_pointer_config = {
176 	.common_config.event_msgq =
177 		&LVGL_INPUT_EVENT_MSGQ(LVGL_CHOSEN_TOUCH_POINTER_DEV_ID, pointer),
178 #ifdef CONFIG_LV_Z_POINTER_FROM_CHOSEN_TOUCH_INFER_DISPLAY
179 	.common_config.display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)),
180 #endif /* CONFIG_LV_Z_POINTER_FROM_CHOSEN_TOUCH_INFER_DISPLAY */
181 };
182 static struct lvgl_pointer_input_data chosen_touch_pointer_data;
183 
184 DEVICE_DEFINE(LVGL_CHOSEN_TOUCH_POINTER_DEV_ID, STRINGIFY(LVGL_CHOSEN_TOUCH_POINTER_DEV_ID), NULL,
185 							  NULL, &chosen_touch_pointer_data,
186 							  &chosen_touch_pointer_config, POST_KERNEL,
187 							  CONFIG_INPUT_INIT_PRIORITY, NULL);
188 
lvgl_pointer_from_chosen_get(void)189 const struct device *lvgl_pointer_from_chosen_get(void)
190 {
191 	return DEVICE_GET(LVGL_CHOSEN_TOUCH_POINTER_DEV_ID);
192 }
193 
194 #endif /* CONFIG_LV_Z_POINTER_FROM_CHOSEN_TOUCH */
195