1 /**
2  * @file lv_uefi_indev.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "../../lvgl.h"
11 #include "../../stdlib/lv_mem.h"
12 #include "../../misc/lv_types.h"
13 #include "../../misc/lv_text.h"
14 
15 #if LV_USE_UEFI
16 
17 #include "lv_uefi_indev.h"
18 #include "lv_uefi_private.h"
19 
20 /*********************
21  *      DEFINES
22  *********************/
23 
24 #define ABSOLUTE_POINTER_INDEV_SIGNATURE 0x41505449
25 
26 /**********************
27  *      TYPEDEFS
28  **********************/
29 
30 typedef struct _lv_uefi_absolute_pointer_handle_context_t {
31     EFI_HANDLE handle;
32     EFI_ABSOLUTE_POINTER_PROTOCOL * interface;
33     lv_point_t range;   /**< The touchscreen resolution*/
34     lv_point_t factor_8;    /**< The scaling factor between the touchscreen and the display resolution*/
35 } lv_uefi_absolute_pointer_handle_context_t;
36 
37 typedef struct _lv_uefi_absolute_pointer_context_t {
38     uint32_t signature; /**< Has to be checked to avoid access to a different indev*/
39     lv_point_t display_res;
40     lv_point_t position;
41     lv_ll_t handles;
42 } lv_uefi_absolute_pointer_context_t;
43 
44 /**********************
45  *  STATIC PROTOTYPES
46  **********************/
47 
48 static void _absolute_pointer_indev_event_cb(lv_event_t * e);
49 static void _absolute_pointer_read_cb(lv_indev_t * indev, lv_indev_data_t * data);
50 static void _absolute_pointer_handle_context_free(void * ptr);
51 static void _absolute_pointer_context_free(lv_uefi_absolute_pointer_context_t * indev_ctx);
52 static bool _absolute_pointer_interface_is_valid(const EFI_ABSOLUTE_POINTER_PROTOCOL * interface);
53 static void _absolute_pointer_read(lv_uefi_absolute_pointer_context_t * indev_ctx,
54                                    lv_uefi_absolute_pointer_handle_context_t * handle_ctx, bool * was_pressed);
55 
56 /**********************
57  *  STATIC VARIABLES
58  **********************/
59 
60 static EFI_GUID _uefi_guid_absolute_pointer = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID;
61 
62 /**********************
63  *      MACROS
64  **********************/
65 
66 /**********************
67  *   GLOBAL FUNCTIONS
68  **********************/
69 
70 /**
71  * @brief Create a LVGL indev object.
72  * @param display_res The resolution of the display in pixels, needed to scale the input.
73  * @return The created LVGL indev object.
74 */
lv_uefi_absolute_pointer_indev_create(lv_point_t * display_res)75 lv_indev_t * lv_uefi_absolute_pointer_indev_create(lv_point_t * display_res)
76 {
77     lv_indev_t * indev = NULL;
78     lv_uefi_absolute_pointer_context_t * indev_ctx = NULL;
79 
80     indev_ctx = lv_calloc(1, sizeof(lv_uefi_absolute_pointer_context_t));
81     LV_ASSERT_MALLOC(indev_ctx);
82 
83     indev_ctx->signature = ABSOLUTE_POINTER_INDEV_SIGNATURE;
84 
85     if(display_res != NULL) {
86         indev_ctx->display_res.x = display_res->x;
87         indev_ctx->display_res.y = display_res->y;
88     }
89     else {
90         indev_ctx->display_res.x = lv_display_get_horizontal_resolution(lv_display_get_default());
91         indev_ctx->display_res.y = lv_display_get_vertical_resolution(lv_display_get_default());
92     }
93 
94     lv_ll_init(&indev_ctx->handles, sizeof(lv_uefi_absolute_pointer_handle_context_t));
95 
96     indev = lv_indev_create();
97     lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
98     lv_indev_set_user_data(indev, indev_ctx);
99     lv_indev_add_event_cb(indev, _absolute_pointer_indev_event_cb, LV_EVENT_DELETE, indev);
100     lv_indev_set_read_cb(indev, _absolute_pointer_read_cb);
101 
102     return indev;
103 }
104 
105 /**
106  * @brief Add an EFI_ABSOLUTE_POINTER_PROTOCOL interface to the indev.
107  * @param indev Indev that was created with lv_uefi_absolute_pointer_indev_create.
108  * @param handle The handle on which an instance of the EFI_ABSOLUTE_POINTER_PROTOCOL protocol is installed.
109  * @return True if the interface was added.
110 */
lv_uefi_absolute_pointer_indev_add_handle(lv_indev_t * indev,EFI_HANDLE handle)111 bool lv_uefi_absolute_pointer_indev_add_handle(lv_indev_t * indev, EFI_HANDLE handle)
112 {
113     EFI_ABSOLUTE_POINTER_PROTOCOL * interface = NULL;
114     lv_uefi_absolute_pointer_handle_context_t * handle_ctx = NULL;
115 
116     lv_uefi_absolute_pointer_context_t * indev_ctx = (lv_uefi_absolute_pointer_context_t *)lv_indev_get_user_data(indev);
117     LV_ASSERT_NULL(indev_ctx);
118 
119     if(indev_ctx->signature != ABSOLUTE_POINTER_INDEV_SIGNATURE) return false;
120 
121     interface = (EFI_ABSOLUTE_POINTER_PROTOCOL *)lv_uefi_protocol_open(handle, &_uefi_guid_absolute_pointer);
122     if(!_absolute_pointer_interface_is_valid(interface)) {
123         lv_uefi_protocol_close(handle, &_uefi_guid_absolute_pointer);
124         LV_LOG_WARN("[lv_uefi] The ABSOLUTE_POINTER interface is not valid.");
125         return false;
126     }
127 
128     handle_ctx = (lv_uefi_absolute_pointer_handle_context_t *) lv_ll_ins_head(&indev_ctx->handles);
129     LV_ASSERT_MALLOC(handle_ctx);
130 
131     handle_ctx->handle = handle;
132     handle_ctx->interface = interface;
133     handle_ctx->range.x = handle_ctx->interface->Mode->AbsoluteMaxX -
134                               handle_ctx->interface->Mode->AbsoluteMinX;
135     handle_ctx->range.y = handle_ctx->interface->Mode->AbsoluteMaxY -
136                               handle_ctx->interface->Mode->AbsoluteMinY;
137 
138     handle_ctx->factor_8.x = (indev_ctx->display_res.x << 8) / handle_ctx->range.x;
139     handle_ctx->factor_8.y = (indev_ctx->display_res.y << 8) / handle_ctx->range.y;
140 
141     return true;
142 }
143 
144 /**
145  * @brief Add all available EFI_ABSOLUTE_POINTER_PROTOCOL interfaces to the indev.
146  * @param indev Indev that was created with lv_uefi_absolute_pointer_indev_create.
147 */
lv_uefi_absolute_pointer_indev_add_all(lv_indev_t * indev)148 void lv_uefi_absolute_pointer_indev_add_all(lv_indev_t * indev)
149 {
150     EFI_STATUS status;
151     EFI_HANDLE * handles = NULL;
152     UINTN no_handles;
153     UINTN index;
154 
155     lv_uefi_absolute_pointer_context_t * indev_ctx = (lv_uefi_absolute_pointer_context_t *)lv_indev_get_user_data(indev);
156     LV_ASSERT_NULL(indev_ctx);
157 
158     if(indev_ctx->signature != ABSOLUTE_POINTER_INDEV_SIGNATURE) return;
159 
160     status = gLvEfiBS->LocateHandleBuffer(ByProtocol, &_uefi_guid_absolute_pointer, NULL, &no_handles, &handles);
161     if(status != EFI_SUCCESS) {
162         LV_LOG_ERROR("[lv_uefi] LocateHandleBuffer(ABSOLUTE_POINTER) failed with error code %llx.", status);
163         return;
164     }
165 
166     for(index = 0; index < no_handles; index++) {
167         lv_uefi_absolute_pointer_indev_add_handle(indev, handles[index]);
168     }
169 
170     if(handles != NULL) gLvEfiBS->FreePool(handles);
171 }
172 
173 /**********************
174  *   STATIC FUNCTIONS
175  **********************/
176 
_absolute_pointer_indev_event_cb(lv_event_t * e)177 static void _absolute_pointer_indev_event_cb(lv_event_t * e)
178 {
179     lv_indev_t * indev;
180     lv_uefi_absolute_pointer_context_t * indev_ctx;
181 
182     if(lv_event_get_code(e) != LV_EVENT_DELETE) return;
183 
184     indev = (lv_indev_t *)lv_event_get_user_data(e);
185     if(indev == NULL) return;
186 
187     indev_ctx = (lv_uefi_absolute_pointer_context_t *)lv_indev_get_user_data(indev);
188     lv_indev_set_user_data(indev, NULL);
189 
190     if(indev_ctx != NULL) _absolute_pointer_context_free(indev_ctx);
191 }
192 
_absolute_pointer_read_cb(lv_indev_t * indev,lv_indev_data_t * data)193 static void _absolute_pointer_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
194 {
195     void * node = NULL;
196 
197     lv_uefi_absolute_pointer_context_t * indev_ctx = (lv_uefi_absolute_pointer_context_t *)lv_indev_get_user_data(indev);
198     LV_ASSERT_NULL(indev_ctx);
199 
200     /* Read from all registered devices */
201     for(node = lv_ll_get_head(&indev_ctx->handles); node != NULL; node = lv_ll_get_next(&indev_ctx->handles, node)) {
202         lv_uefi_absolute_pointer_handle_context_t * handle_ctx = (lv_uefi_absolute_pointer_handle_context_t *) node;
203         bool was_pressed = false;
204 
205         _absolute_pointer_read(indev_ctx, handle_ctx, &was_pressed);
206 
207         data->state |= was_pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
208     }
209 
210     /* Sanitize the events position */
211     if(indev_ctx->position.x < 0) {
212         indev_ctx->position.x = 0;
213     }
214     else if(indev_ctx->position.x > indev_ctx->display_res.x - 1) {
215         indev_ctx->position.x = indev_ctx->display_res.x - 1;
216     }
217 
218     if(indev_ctx->position.y < 0) {
219         indev_ctx->position.y = 0;
220     }
221     else if(indev_ctx->position.y > indev_ctx->display_res.y - 1) {
222         indev_ctx->position.y = indev_ctx->display_res.y - 1;
223     }
224 
225     data->point.x = indev_ctx->position.x;
226     data->point.y = indev_ctx->position.y;
227 
228     data->continue_reading = FALSE;
229 }
230 
_absolute_pointer_context_free(lv_uefi_absolute_pointer_context_t * indev_ctx)231 static void _absolute_pointer_context_free(lv_uefi_absolute_pointer_context_t * indev_ctx)
232 {
233     if(indev_ctx == NULL) return;
234     lv_ll_clear_custom(&indev_ctx->handles, _absolute_pointer_handle_context_free);
235     lv_free(indev_ctx);
236 }
237 
_absolute_pointer_interface_is_valid(const EFI_ABSOLUTE_POINTER_PROTOCOL * interface)238 static bool _absolute_pointer_interface_is_valid(const EFI_ABSOLUTE_POINTER_PROTOCOL * interface)
239 {
240     if(interface == NULL) return FALSE;
241     if(interface->Mode == NULL) return FALSE;
242     if(interface->Mode->AbsoluteMaxX <= interface->Mode->AbsoluteMinX) return FALSE;
243     if(interface->Mode->AbsoluteMaxY <= interface->Mode->AbsoluteMinY) return FALSE;
244     return TRUE;
245 }
246 
_absolute_pointer_handle_context_free(void * ptr)247 static void _absolute_pointer_handle_context_free(void * ptr)
248 {
249     lv_uefi_absolute_pointer_handle_context_t * handle_ctx = (lv_uefi_absolute_pointer_handle_context_t *) ptr;
250 
251     if(handle_ctx == NULL) return;
252     if(handle_ctx->interface) lv_uefi_protocol_close(handle_ctx->handle, &_uefi_guid_absolute_pointer);
253     lv_free(handle_ctx);
254 }
255 
_absolute_pointer_read(lv_uefi_absolute_pointer_context_t * indev_ctx,lv_uefi_absolute_pointer_handle_context_t * handle_ctx,bool * was_pressed)256 static void _absolute_pointer_read(lv_uefi_absolute_pointer_context_t * indev_ctx,
257                                    lv_uefi_absolute_pointer_handle_context_t * handle_ctx, bool * was_pressed)
258 {
259     EFI_STATUS status;
260     EFI_ABSOLUTE_POINTER_STATE state;
261     lv_point_t pointer_pos;
262 
263     LV_ASSERT_NULL(indev_ctx);
264     LV_ASSERT_NULL(handle_ctx);
265     LV_ASSERT_NULL(was_pressed);
266 
267     status = handle_ctx->interface->GetState(
268                      handle_ctx->interface,
269                      &state);
270     if(status == EFI_NOT_READY) return;
271     if(status != EFI_SUCCESS) {
272         LV_LOG_ERROR("[lv_uefi] GetState failed.");
273         return;
274     }
275 
276     /* verify the state */
277     if(state.CurrentX < handle_ctx->interface->Mode->AbsoluteMinX) return;
278     if(state.CurrentY < handle_ctx->interface->Mode->AbsoluteMinY) return;
279 
280     pointer_pos.x = state.CurrentX - handle_ctx->interface->Mode->AbsoluteMinX;
281     pointer_pos.y = state.CurrentY - handle_ctx->interface->Mode->AbsoluteMinY;
282 
283     indev_ctx->position.x = (pointer_pos.x * handle_ctx->factor_8.x) >> 8;
284     indev_ctx->position.y = (pointer_pos.y * handle_ctx->factor_8.y) >> 8;
285 
286     /* Set the state to pressed if one of the interfaces reports a press */
287     *was_pressed = (state.ActiveButtons & EFI_ABSP_TouchActive) != 0;
288 }
289 
290 #endif