| 146 | } |
| 147 | |
| 148 | static void usbHidInputTask(void* arg) { |
| 149 | auto* ctx = static_cast<UsbHidInputCtx*>(arg); |
| 150 | LOGGER.info("started"); |
| 151 | |
| 152 | while (!lv_is_initialized()) { |
| 153 | vTaskDelay(pdMS_TO_TICKS(100)); |
| 154 | } |
| 155 | |
| 156 | if (lock()) { |
| 157 | ctx->mouse_cursor = lv_image_create(lv_layer_sys()); |
| 158 | lv_obj_remove_flag(ctx->mouse_cursor, LV_OBJ_FLAG_CLICKABLE); |
| 159 | lv_image_set_src(ctx->mouse_cursor, TT_ASSETS_UI_CURSOR); |
| 160 | lv_obj_add_flag(ctx->mouse_cursor, LV_OBJ_FLAG_HIDDEN); |
| 161 | |
| 162 | ctx->mouse_indev = lv_indev_create(); |
| 163 | lv_indev_set_type(ctx->mouse_indev, LV_INDEV_TYPE_POINTER); |
| 164 | lv_indev_set_read_cb(ctx->mouse_indev, mouse_read_cb); |
| 165 | lv_indev_set_user_data(ctx->mouse_indev, ctx); |
| 166 | lv_indev_set_cursor(ctx->mouse_indev, ctx->mouse_cursor); |
| 167 | |
| 168 | ctx->kb_indev = lv_indev_create(); |
| 169 | lv_indev_set_type(ctx->kb_indev, LV_INDEV_TYPE_KEYPAD); |
| 170 | lv_indev_set_read_cb(ctx->kb_indev, keyboard_read_cb); |
| 171 | lv_indev_set_user_data(ctx->kb_indev, ctx); |
| 172 | lv_indev_set_group(ctx->kb_indev, lv_group_get_default()); |
| 173 | |
| 174 | unlock(); |
| 175 | LOGGER.info("LVGL input devices registered"); |
| 176 | } else { |
| 177 | LOGGER.warn("could not acquire LVGL lock for indev registration"); |
| 178 | } |
| 179 | |
| 180 | // Drain the HID event queue and route events to the appropriate destinations |
| 181 | while (ctx->running) { |
| 182 | UsbHidEvent hid_evt; |
| 183 | if (xQueueReceive(ctx->hid_queue, &hid_evt, pdMS_TO_TICKS(100)) != pdTRUE) { |
| 184 | if (!ctx->subscribed) { |
| 185 | struct Device* hid_dev = device_find_first_active_by_type(&USB_HOST_HID_TYPE); |
| 186 | if (hid_dev) ctx->subscribed = usb_host_hid_subscribe(hid_dev, ctx->hid_queue); |
| 187 | } |
| 188 | continue; |
| 189 | } |
| 190 | |
| 191 | switch (hid_evt.type) { |
| 192 | case USB_HID_EVENT_KEY: { |
| 193 | KeyEvent key_evt = { hid_evt.key.key_code, hid_evt.key.pressed }; |
| 194 | xQueueSend(ctx->key_queue, &key_evt, 0); |
| 195 | break; |
| 196 | } |
| 197 | case USB_HID_EVENT_MOUSE_MOVE: { |
| 198 | lv_display_t* disp = lv_display_get_default(); |
| 199 | if (!disp) break; |
| 200 | // Use logical (post-rotation) resolution so clamping matches LVGL's coordinate space |
| 201 | int32_t w = lv_display_get_horizontal_resolution(disp); |
| 202 | int32_t h = lv_display_get_vertical_resolution(disp); |
| 203 | int32_t nx = ctx->mouse_x.load() + hid_evt.mouse_move.dx; |
| 204 | int32_t ny = ctx->mouse_y.load() + hid_evt.mouse_move.dy; |
| 205 | if (nx < 0) nx = 0; |
nothing calls this directly
no test coverage detected