/** * @file lv_x11_input.c * */ /********************* * INCLUDES *********************/ #include "lv_x11.h" #if LV_USE_X11 #include #include #include #include #include "../../widgets/image/lv_image.h" /********************* * DEFINES *********************/ #define MIN(A, B) ((A) < (B) ? (A) : (B)) /********************** * TYPEDEFS **********************/ typedef struct _x11_inp_data { /* LVGL related information */ lv_group_t * inp_group; /**< input group for X input elements */ lv_indev_t * keyboard; /**< keyboard input device object */ lv_indev_t * mousepointer; /**< mouse input device object */ lv_indev_t * mousewheel; /**< encoder input device object */ lv_timer_t * timer; /**< timer object for @ref x11_event_handler */ /* user input related information */ char kb_buffer[32]; /**< keyboard buffer for X keyboard inputs */ lv_point_t mouse_pos; /**< current reported mouse position */ bool left_mouse_btn; /**< current state of left mouse button */ bool right_mouse_btn; /**< current state of right mouse button */ bool wheel_mouse_btn; /**< current state of wheel (=middle) mouse button */ int16_t wheel_cnt; /**< mouse wheel increments */ } x11_inp_data_t; /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * STATIC FUNCTIONS **********************/ /** * X11 input event handler, predicated to fetch and handle only input related events * (MotionNotify, ButtonPress/Release, KeyPress/Release) */ static int is_inp_event(Display * disp, XEvent * event, XPointer arg) { LV_UNUSED(disp); LV_UNUSED(arg); return !(event->type == Expose || (event->type >= DestroyNotify && event->type <= CirculateNotify) /* events from StructureNotifyMask */ || event->type == ClientMessage); } static void x11_inp_event_handler(lv_timer_t * t) { lv_display_t * disp = lv_timer_get_user_data(t); _x11_user_hdr_t * disp_hdr = lv_display_get_driver_data(disp); x11_inp_data_t * xd = disp_hdr->inp_data; /* handle all outstanding X events */ XEvent event; while(XCheckIfEvent(disp_hdr->display, &event, is_inp_event, NULL)) { LV_LOG_TRACE("Input Event %d", event.type); switch(event.type) { case MotionNotify: xd->mouse_pos.x = event.xmotion.x; xd->mouse_pos.y = event.xmotion.y; break; case ButtonPress: switch(event.xbutton.button) { case Button1: xd->left_mouse_btn = true; break; case Button2: xd->wheel_mouse_btn = true; break; case Button3: xd->right_mouse_btn = true; break; case Button4: /* Scrolled up */ xd->wheel_cnt--; break; case Button5: /* Scrolled down */ xd->wheel_cnt++; break; default: LV_LOG_WARN("unhandled button press : %d", event.xbutton.button); } break; case ButtonRelease: switch(event.xbutton.button) { case Button1: xd->left_mouse_btn = false; break; case Button2: xd->wheel_mouse_btn = false; break; case Button3: xd->right_mouse_btn = false; break; } break; case KeyPress: { size_t len = strlen(xd->kb_buffer); if(len < (sizeof(xd->kb_buffer) - 2 /* space for 1 char + '\0' */)) { KeySym key; int n = XLookupString(&event.xkey, &xd->kb_buffer[len], sizeof(xd->kb_buffer) - (len + 1), &key, NULL); n += !!key; switch(key) { case XK_Home: case XK_KP_Home: xd->kb_buffer[len] = LV_KEY_HOME; break; case XK_Left: case XK_KP_Left: xd->kb_buffer[len] = LV_KEY_LEFT; break; case XK_Up: case XK_KP_Up: xd->kb_buffer[len] = LV_KEY_UP; break; case XK_Right: case XK_KP_Right: xd->kb_buffer[len] = LV_KEY_RIGHT; break; case XK_Down: case XK_KP_Down: xd->kb_buffer[len] = LV_KEY_DOWN; break; case XK_Prior: case XK_KP_Prior: xd->kb_buffer[len] = LV_KEY_PREV; break; case XK_Next: case XK_KP_Next: xd->kb_buffer[len] = LV_KEY_NEXT; break; case XK_End: case XK_KP_End: xd->kb_buffer[len] = LV_KEY_END; break; case XK_BackSpace: xd->kb_buffer[len] = LV_KEY_BACKSPACE; break; case XK_Escape: xd->kb_buffer[len] = LV_KEY_ESC; break; case XK_Delete: case XK_KP_Delete: xd->kb_buffer[len] = LV_KEY_DEL; break; case XK_KP_Enter: xd->kb_buffer[len] = LV_KEY_ENTER; break; } xd->kb_buffer[len + n] = '\0'; } } break; case KeyRelease: break; default: LV_LOG_WARN("unhandled x11 event: %d", event.type); } } } /** * event called by lvgl display if display has been closed (@ref lv_display_delete has been called) * @param[in] e event data, containing lv_display_t object */ static void x11_inp_delete_evt_cb(lv_event_t * e) { x11_inp_data_t * xd = (x11_inp_data_t *)lv_event_get_user_data(e); lv_timer_delete(xd->timer); lv_free(xd); } /** * create the local data/timers for the X11 input functionality. * extracts the user data information from lv_display_t object and initializes the input user object on 1st use. * @param[in] disp the created X11 display object from @lv_x11_window_create * @return pointer to the local user data object @x11_inp_data_t */ static x11_inp_data_t * x11_input_get_user_data(lv_display_t * disp) { _x11_user_hdr_t * disp_hdr = lv_display_get_driver_data(disp); LV_ASSERT_NULL(disp_hdr); x11_inp_data_t ** inp_data = &disp_hdr->inp_data; /* create input data set if initial call */ if(NULL == *inp_data) { *inp_data = lv_malloc_zeroed(sizeof(x11_inp_data_t)); LV_ASSERT_MALLOC(*inp_data); if(NULL != *inp_data) { /* initialize timer callback for X11 kb/mouse input event reading */ (*inp_data)->timer = lv_timer_create(x11_inp_event_handler, 1, disp); lv_display_add_event_cb(disp, x11_inp_delete_evt_cb, LV_EVENT_DELETE, *inp_data); } } return *inp_data; } static void x11_keyboard_read_cb(lv_indev_t * indev, lv_indev_data_t * data) { lv_display_t * disp = lv_indev_get_driver_data(indev); x11_inp_data_t * xd = x11_input_get_user_data(disp); size_t len = strlen(xd->kb_buffer); if(len > 0) { data->state = LV_INDEV_STATE_PRESSED; data->key = xd->kb_buffer[0]; memmove(xd->kb_buffer, xd->kb_buffer + 1, len); data->continue_reading = (len > 0); } else { data->state = LV_INDEV_STATE_RELEASED; } } static void x11_mouse_read_cb(lv_indev_t * indev, lv_indev_data_t * data) { lv_display_t * disp = lv_indev_get_driver_data(indev); x11_inp_data_t * xd = x11_input_get_user_data(disp); int32_t hor_res = lv_display_get_horizontal_resolution(disp); int32_t ver_res = lv_display_get_vertical_resolution(disp); xd->mouse_pos.x = MIN(xd->mouse_pos.x, hor_res - 1); xd->mouse_pos.y = MIN(xd->mouse_pos.y, ver_res - 1); data->point = xd->mouse_pos; data->state = xd->left_mouse_btn ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; } static void x11_mousewheel_read_cb(lv_indev_t * indev, lv_indev_data_t * data) { lv_display_t * disp = lv_indev_get_driver_data(indev); x11_inp_data_t * xd = x11_input_get_user_data(disp); data->state = xd->wheel_mouse_btn ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; data->enc_diff = xd->wheel_cnt; xd->wheel_cnt = 0; } static lv_indev_t * lv_x11_keyboard_create(lv_display_t * disp) { lv_indev_t * indev = lv_indev_create(); LV_ASSERT_NULL(indev); if(NULL != indev) { lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD); lv_indev_set_read_cb(indev, x11_keyboard_read_cb); lv_indev_set_driver_data(indev, disp); } return indev; } static lv_indev_t * lv_x11_mouse_create(lv_display_t * disp, lv_image_dsc_t const * symb) { lv_indev_t * indev = lv_indev_create(); if(NULL != indev) { lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); lv_indev_set_read_cb(indev, x11_mouse_read_cb); lv_indev_set_driver_data(indev, disp); /* optional mouse cursor symbol */ if(NULL != symb) { lv_obj_t * mouse_cursor = lv_image_create(lv_screen_active()); lv_image_set_src(mouse_cursor, symb); lv_indev_set_cursor(indev, mouse_cursor); } } return indev; } static lv_indev_t * lv_x11_mousewheel_create(lv_display_t * disp) { lv_indev_t * indev = lv_indev_create(); if(NULL != indev) { lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER); lv_indev_set_read_cb(indev, x11_mousewheel_read_cb); lv_indev_set_driver_data(indev, disp); } return indev; } /********************** * GLOBAL FUNCTIONS **********************/ void lv_x11_inputs_create(lv_display_t * disp, lv_image_dsc_t const * mouse_img) { x11_inp_data_t * xd = x11_input_get_user_data(disp); LV_ASSERT_NULL(xd); xd->inp_group = lv_group_create(); lv_group_set_default(xd->inp_group); xd->mousepointer = lv_x11_mouse_create(disp, mouse_img); lv_indev_set_group(xd->mousepointer, xd->inp_group); xd->mousewheel = lv_x11_mousewheel_create(disp); lv_indev_set_group(xd->mousewheel, xd->inp_group); xd->keyboard = lv_x11_keyboard_create(disp); lv_indev_set_group(xd->keyboard, xd->inp_group); } #endif /*LV_USE_X11*/