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 SIMPLE_POINTER_INDEV_SIGNATURE 0x53505449
25
26 /**********************
27 * TYPEDEFS
28 **********************/
29
30 typedef struct _lv_uefi_simple_pointer_handle_context_t {
31 EFI_HANDLE handle;
32 EFI_SIMPLE_POINTER_PROTOCOL * interface;
33 lv_point_t pixel_per_step_8; /**< How many pixels does the mouse cursor move in one step*/
34 } lv_uefi_simple_pointer_handle_context_t;
35
36 typedef struct _lv_uefi_simple_pointer_context_t {
37 uint32_t signature; /**< Has to be checked to avoid access to a different indev*/
38 lv_point_t display_res;
39 lv_point_t position;
40 lv_ll_t handles;
41 } lv_uefi_simple_pointer_context_t;
42
43 /**********************
44 * STATIC PROTOTYPES
45 **********************/
46
47 static void _simple_pointer_indev_event_cb(lv_event_t * e);
48 static void _simple_pointer_read_cb(lv_indev_t * indev, lv_indev_data_t * data);
49 static void _simple_pointer_handle_context_free(void * ptr);
50 static void _simple_pointer_context_free(lv_uefi_simple_pointer_context_t * indev_ctx);
51 static bool _simple_pointer_interface_is_valid(const EFI_SIMPLE_POINTER_PROTOCOL * interface);
52 static void _simple_pointer_read(lv_uefi_simple_pointer_context_t * indev_ctx,
53 lv_uefi_simple_pointer_handle_context_t * handle_ctx, bool * was_pressed);
54
55 /**********************
56 * STATIC VARIABLES
57 **********************/
58
59 static EFI_GUID _uefi_guid_simple_pointer = EFI_SIMPLE_POINTER_PROTOCOL_GUID;
60
61 /**********************
62 * MACROS
63 **********************/
64
65 /**********************
66 * GLOBAL FUNCTIONS
67 **********************/
68
69 /**
70 * @brief Create an indev object.
71 * @param display_res The resolution of the display in pixels, needed to scale the input.
72 * If NULL the resolution of the current default display will be used.
73 * @return The created LVGL indev object.
74 */
lv_uefi_simple_pointer_indev_create(lv_point_t * display_res)75 lv_indev_t * lv_uefi_simple_pointer_indev_create(lv_point_t * display_res)
76 {
77 lv_indev_t * indev = NULL;
78 lv_uefi_simple_pointer_context_t * indev_ctx = NULL;
79
80 indev_ctx = lv_calloc(1, sizeof(lv_uefi_simple_pointer_context_t));
81 LV_ASSERT_MALLOC(indev_ctx);
82
83 indev_ctx->signature = SIMPLE_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_simple_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, _simple_pointer_indev_event_cb, LV_EVENT_DELETE, indev);
100 lv_indev_set_read_cb(indev, _simple_pointer_read_cb);
101
102 return indev;
103 }
104
105 /**
106 * @brief Add an EFI_SIMPLE_POINTER_PROTOCOL interface to the indev.
107 * @param indev Indev that was created with lv_uefi_simple_pointer_indev_create.
108 * @param handle The handle on which an instance of the EFI_SIMPLE_POINTER_PROTOCOL protocol is installed.
109 * @return True if the interface was added.
110 */
lv_uefi_simple_pointer_indev_add_handle(lv_indev_t * indev,EFI_HANDLE handle)111 bool lv_uefi_simple_pointer_indev_add_handle(lv_indev_t * indev, EFI_HANDLE handle)
112 {
113 EFI_SIMPLE_POINTER_PROTOCOL * interface = NULL;
114 lv_uefi_simple_pointer_handle_context_t * handle_ctx = NULL;
115
116 lv_uefi_simple_pointer_context_t * indev_ctx = (lv_uefi_simple_pointer_context_t *)lv_indev_get_user_data(indev);
117 LV_ASSERT_NULL(indev_ctx);
118
119 if(indev_ctx->signature != SIMPLE_POINTER_INDEV_SIGNATURE) return false;
120
121 interface = (EFI_SIMPLE_POINTER_PROTOCOL *)lv_uefi_protocol_open(handle, &_uefi_guid_simple_pointer);
122 if(!_simple_pointer_interface_is_valid(interface)) {
123 lv_uefi_protocol_close(handle, &_uefi_guid_simple_pointer);
124 LV_LOG_WARN("[lv_uefi] The SIMPLE_POINTER interface is not valid.");
125 return false;
126 }
127
128 handle_ctx = (lv_uefi_simple_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->pixel_per_step_8.x = (((indev_ctx->display_res.x) << 8) / 50) /
134 interface->Mode->ResolutionX;
135 handle_ctx->pixel_per_step_8.y = (((indev_ctx->display_res.y) << 8) / 50) /
136 interface->Mode->ResolutionY;
137
138 return true;
139 }
140
141 /**
142 * @brief Add all available EFI_SIMPLE_POINTER_PROTOCOL interfaces to the indev.
143 * @param indev Indev that was created with lv_uefi_simple_pointer_indev_create.
144 */
lv_uefi_simple_pointer_indev_add_all(lv_indev_t * indev)145 void lv_uefi_simple_pointer_indev_add_all(lv_indev_t * indev)
146 {
147 EFI_STATUS status;
148 EFI_HANDLE * handles = NULL;
149 UINTN no_handles;
150 UINTN index;
151
152 lv_uefi_simple_pointer_context_t * indev_ctx = (lv_uefi_simple_pointer_context_t *)lv_indev_get_user_data(indev);
153 LV_ASSERT_NULL(indev_ctx);
154
155 if(indev_ctx->signature != SIMPLE_POINTER_INDEV_SIGNATURE) return;
156
157 status = gLvEfiBS->LocateHandleBuffer(ByProtocol, &_uefi_guid_simple_pointer, NULL, &no_handles, &handles);
158 if(status != EFI_SUCCESS) {
159 LV_LOG_ERROR("[lv_uefi] LocateHandleBuffer(SIMPLE_POINTER) failed with error code %llx.", status);
160 return;
161 }
162
163 for(index = 0; index < no_handles; index++) {
164 lv_uefi_simple_pointer_indev_add_handle(indev, handles[index]);
165 }
166
167 if(handles != NULL) gLvEfiBS->FreePool(handles);
168 }
169
170 /**********************
171 * STATIC FUNCTIONS
172 **********************/
173
_simple_pointer_indev_event_cb(lv_event_t * e)174 static void _simple_pointer_indev_event_cb(lv_event_t * e)
175 {
176 lv_indev_t * indev;
177 lv_uefi_simple_pointer_context_t * indev_ctx;
178
179 if(lv_event_get_code(e) != LV_EVENT_DELETE) return;
180
181 indev = (lv_indev_t *)lv_event_get_user_data(e);
182 if(indev == NULL) return;
183
184 indev_ctx = (lv_uefi_simple_pointer_context_t *)lv_indev_get_user_data(indev);
185 lv_indev_set_user_data(indev, NULL);
186
187 if(indev_ctx != NULL) _simple_pointer_context_free(indev_ctx);
188 }
189
_simple_pointer_read_cb(lv_indev_t * indev,lv_indev_data_t * data)190 static void _simple_pointer_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
191 {
192 void * node = NULL;
193
194 lv_uefi_simple_pointer_context_t * indev_ctx = (lv_uefi_simple_pointer_context_t *)lv_indev_get_user_data(indev);
195 LV_ASSERT_NULL(indev_ctx);
196
197 /* Read from all registered devices */
198 for(node = lv_ll_get_head(&indev_ctx->handles); node != NULL; node = lv_ll_get_next(&indev_ctx->handles, node)) {
199 lv_uefi_simple_pointer_handle_context_t * handle_ctx = (lv_uefi_simple_pointer_handle_context_t *) node;
200 bool was_pressed = false;
201
202 _simple_pointer_read(indev_ctx, handle_ctx, &was_pressed);
203
204 data->state |= was_pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
205 }
206
207 /* Sanitize the events position */
208 if(indev_ctx->position.x < 0) {
209 indev_ctx->position.x = 0;
210 }
211 else if(indev_ctx->position.x > indev_ctx->display_res.x - 1) {
212 indev_ctx->position.x = indev_ctx->display_res.x - 1;
213 }
214
215 if(indev_ctx->position.y < 0) {
216 indev_ctx->position.y = 0;
217 }
218 else if(indev_ctx->position.y > indev_ctx->display_res.y - 1) {
219 indev_ctx->position.y = indev_ctx->display_res.y - 1;
220 }
221
222 data->point.x = indev_ctx->position.x;
223 data->point.y = indev_ctx->position.y;
224
225 data->continue_reading = FALSE;
226 }
227
_simple_pointer_context_free(lv_uefi_simple_pointer_context_t * indev_ctx)228 static void _simple_pointer_context_free(lv_uefi_simple_pointer_context_t * indev_ctx)
229 {
230 if(indev_ctx == NULL) return;
231 lv_ll_clear_custom(&indev_ctx->handles, _simple_pointer_handle_context_free);
232 lv_free(indev_ctx);
233 }
234
_simple_pointer_interface_is_valid(const EFI_SIMPLE_POINTER_PROTOCOL * interface)235 static bool _simple_pointer_interface_is_valid(const EFI_SIMPLE_POINTER_PROTOCOL * interface)
236 {
237 if(interface == NULL) return FALSE;
238 if(interface->Mode == NULL) return FALSE;
239 if(interface->Mode->ResolutionX == 0) return FALSE;
240 if(interface->Mode->ResolutionX >= 256) return FALSE;
241 if(interface->Mode->ResolutionY == 0) return FALSE;
242 if(interface->Mode->ResolutionY >= 256) return FALSE;
243 return TRUE;
244 }
245
_simple_pointer_handle_context_free(void * ptr)246 static void _simple_pointer_handle_context_free(void * ptr)
247 {
248 lv_uefi_simple_pointer_handle_context_t * handle_ctx = (lv_uefi_simple_pointer_handle_context_t *)ptr;
249
250 if(handle_ctx == NULL) return;
251 if(handle_ctx->interface) lv_uefi_protocol_close(handle_ctx->handle, &_uefi_guid_simple_pointer);
252 lv_free(handle_ctx);
253 }
254
_simple_pointer_read(lv_uefi_simple_pointer_context_t * indev_ctx,lv_uefi_simple_pointer_handle_context_t * handle_ctx,bool * was_pressed)255 static void _simple_pointer_read(lv_uefi_simple_pointer_context_t * indev_ctx,
256 lv_uefi_simple_pointer_handle_context_t * handle_ctx, bool * was_pressed)
257 {
258 EFI_STATUS status;
259 EFI_SIMPLE_POINTER_STATE state;
260 lv_point_t pointer_mov;
261
262 LV_ASSERT_NULL(indev_ctx);
263 LV_ASSERT_NULL(handle_ctx);
264 LV_ASSERT_NULL(was_pressed);
265
266 status = handle_ctx->interface->GetState(
267 handle_ctx->interface,
268 &state);
269 if(status == EFI_NOT_READY) return;
270 if(status != EFI_SUCCESS) {
271 LV_LOG_ERROR("[lv_uefi] GetState failed.");
272 return;
273 }
274
275 pointer_mov.x = (state.RelativeMovementX * handle_ctx->pixel_per_step_8.x) >> 8;
276 pointer_mov.y = (state.RelativeMovementY * handle_ctx->pixel_per_step_8.y) >> 8;
277
278 indev_ctx->position.x += pointer_mov.x;
279 indev_ctx->position.y += pointer_mov.y;
280
281 /* Set the state to pressed if one of the interfaces reports a press */
282 *was_pressed = state.LeftButton;
283 }
284
285 #endif