1 /**
2  * @file lv_x11_input.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_x11.h"
10 
11 #if LV_USE_X11
12 
13 #include <string.h>
14 #include <stdbool.h>
15 #include <X11/Xlib.h>
16 #include <X11/Xutil.h>
17 #include "../../widgets/image/lv_image.h"
18 
19 /*********************
20  *      DEFINES
21  *********************/
22 #define MIN(A, B) ((A) < (B) ? (A) : (B))
23 
24 /**********************
25  *      TYPEDEFS
26  **********************/
27 
28 typedef struct _x11_inp_data {
29     /* LVGL related information */
30     lv_group_t * inp_group;      /**< input group for X input elements */
31     lv_indev_t * keyboard;       /**< keyboard input device object */
32     lv_indev_t * mousepointer;   /**< mouse input device object */
33     lv_indev_t * mousewheel;     /**< encoder input device object */
34     lv_timer_t * timer;          /**< timer object for @ref x11_event_handler */
35     /* user input related information */
36     char         kb_buffer[32];   /**< keyboard buffer for X keyboard inputs */
37     lv_point_t   mouse_pos;       /**< current reported mouse position */
38     bool         left_mouse_btn;  /**< current state of left mouse button */
39     bool         right_mouse_btn; /**< current state of right mouse button */
40     bool         wheel_mouse_btn; /**< current state of wheel (=middle) mouse button */
41     int16_t      wheel_cnt;       /**< mouse wheel increments */
42 } x11_inp_data_t;
43 
44 /**********************
45  *  STATIC VARIABLES
46  **********************/
47 
48 /**********************
49  *      MACROS
50  **********************/
51 
52 /**********************
53  *   STATIC FUNCTIONS
54  **********************/
55 
56 /**
57  * X11 input event handler, predicated to fetch and handle only input related events
58  * (MotionNotify, ButtonPress/Release, KeyPress/Release)
59  */
is_inp_event(Display * disp,XEvent * event,XPointer arg)60 static int is_inp_event(Display * disp, XEvent * event, XPointer arg)
61 {
62     LV_UNUSED(disp);
63     LV_UNUSED(arg);
64     return !(event->type == Expose
65              || (event->type >= DestroyNotify && event->type <= CirculateNotify) /* events from StructureNotifyMask */
66              ||  event->type == ClientMessage);
67 }
x11_inp_event_handler(lv_timer_t * t)68 static void x11_inp_event_handler(lv_timer_t * t)
69 {
70     lv_display_t * disp = lv_timer_get_user_data(t);
71     _x11_user_hdr_t * disp_hdr = lv_display_get_driver_data(disp);
72     x11_inp_data_t * xd = disp_hdr->inp_data;
73 
74     /* handle all outstanding X events */
75     XEvent event;
76     while(XCheckIfEvent(disp_hdr->display, &event, is_inp_event, NULL)) {
77         LV_LOG_TRACE("Input Event %d", event.type);
78         switch(event.type) {
79             case MotionNotify:
80                 xd->mouse_pos.x = event.xmotion.x;
81                 xd->mouse_pos.y = event.xmotion.y;
82                 break;
83             case ButtonPress:
84                 switch(event.xbutton.button) {
85                     case Button1:
86                         xd->left_mouse_btn = true;
87                         break;
88                     case Button2:
89                         xd->wheel_mouse_btn = true;
90                         break;
91                     case Button3:
92                         xd->right_mouse_btn = true;
93                         break;
94                     case Button4: /* Scrolled up */
95                         xd->wheel_cnt--;
96                         break;
97                     case Button5: /* Scrolled down */
98                         xd->wheel_cnt++;
99                         break;
100                     default:
101                         LV_LOG_WARN("unhandled button press : %d", event.xbutton.button);
102                 }
103                 break;
104             case ButtonRelease:
105                 switch(event.xbutton.button) {
106                     case Button1:
107                         xd->left_mouse_btn = false;
108                         break;
109                     case Button2:
110                         xd->wheel_mouse_btn = false;
111                         break;
112                     case Button3:
113                         xd->right_mouse_btn = false;
114                         break;
115                 }
116                 break;
117             case KeyPress: {
118                     size_t len = strlen(xd->kb_buffer);
119                     if(len < (sizeof(xd->kb_buffer) - 2 /* space for 1 char + '\0' */)) {
120                         KeySym key;
121                         int n = XLookupString(&event.xkey, &xd->kb_buffer[len], sizeof(xd->kb_buffer) - (len + 1), &key, NULL);
122                         n += !!key;
123                         switch(key) {
124                             case XK_Home:
125                             case XK_KP_Home:
126                                 xd->kb_buffer[len] = LV_KEY_HOME;
127                                 break;
128                             case XK_Left:
129                             case XK_KP_Left:
130                                 xd->kb_buffer[len] = LV_KEY_LEFT;
131                                 break;
132                             case XK_Up:
133                             case XK_KP_Up:
134                                 xd->kb_buffer[len] = LV_KEY_UP;
135                                 break;
136                             case XK_Right:
137                             case XK_KP_Right:
138                                 xd->kb_buffer[len] = LV_KEY_RIGHT;
139                                 break;
140                             case XK_Down:
141                             case XK_KP_Down:
142                                 xd->kb_buffer[len] = LV_KEY_DOWN;
143                                 break;
144                             case XK_Prior:
145                             case XK_KP_Prior:
146                                 xd->kb_buffer[len] = LV_KEY_PREV;
147                                 break;
148                             case XK_Next:
149                             case XK_KP_Next:
150                                 xd->kb_buffer[len] = LV_KEY_NEXT;
151                                 break;
152                             case XK_End:
153                             case XK_KP_End:
154                                 xd->kb_buffer[len] = LV_KEY_END;
155                                 break;
156                             case XK_BackSpace:
157                                 xd->kb_buffer[len] = LV_KEY_BACKSPACE;
158                                 break;
159                             case XK_Escape:
160                                 xd->kb_buffer[len] = LV_KEY_ESC;
161                                 break;
162                             case XK_Delete:
163                             case XK_KP_Delete:
164                                 xd->kb_buffer[len] = LV_KEY_DEL;
165                                 break;
166                             case XK_KP_Enter:
167                                 xd->kb_buffer[len] = LV_KEY_ENTER;
168                                 break;
169                         }
170                         xd->kb_buffer[len + n] = '\0';
171                     }
172                 }
173                 break;
174             case KeyRelease:
175                 break;
176             default:
177                 LV_LOG_WARN("unhandled x11 event: %d", event.type);
178         }
179     }
180 }
181 
182 /**
183  * event called by lvgl display if display has been closed (@ref lv_display_delete has been called)
184  * @param[in] e  event data, containing lv_display_t object
185  */
x11_inp_delete_evt_cb(lv_event_t * e)186 static void x11_inp_delete_evt_cb(lv_event_t * e)
187 {
188     x11_inp_data_t * xd = (x11_inp_data_t *)lv_event_get_user_data(e);
189 
190     lv_timer_delete(xd->timer);
191     lv_free(xd);
192 }
193 
194 /**
195  * create the local data/timers for the X11 input functionality.
196  * extracts the user data information from lv_display_t object and initializes the input user object on 1st use.
197  * @param[in] disp   the created X11 display object from @lv_x11_window_create
198  * @return           pointer to the local user data object @x11_inp_data_t
199  */
x11_input_get_user_data(lv_display_t * disp)200 static x11_inp_data_t * x11_input_get_user_data(lv_display_t * disp)
201 {
202     _x11_user_hdr_t * disp_hdr = lv_display_get_driver_data(disp);
203     LV_ASSERT_NULL(disp_hdr);
204     x11_inp_data_t ** inp_data = &disp_hdr->inp_data;
205 
206     /* create input data set if initial call */
207     if(NULL == *inp_data) {
208         *inp_data = lv_malloc_zeroed(sizeof(x11_inp_data_t));
209         LV_ASSERT_MALLOC(*inp_data);
210         if(NULL != *inp_data) {
211             /* initialize timer callback for X11 kb/mouse input event reading */
212             (*inp_data)->timer = lv_timer_create(x11_inp_event_handler, 1, disp);
213             lv_display_add_event_cb(disp, x11_inp_delete_evt_cb, LV_EVENT_DELETE, *inp_data);
214         }
215     }
216     return *inp_data;
217 }
218 
x11_keyboard_read_cb(lv_indev_t * indev,lv_indev_data_t * data)219 static void x11_keyboard_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
220 {
221     lv_display_t * disp = lv_indev_get_driver_data(indev);
222     x11_inp_data_t * xd = x11_input_get_user_data(disp);
223 
224     size_t len = strlen(xd->kb_buffer);
225     if(len > 0) {
226         data->state = LV_INDEV_STATE_PRESSED;
227         data->key   = xd->kb_buffer[0];
228         memmove(xd->kb_buffer, xd->kb_buffer + 1, len);
229         data->continue_reading = (len > 0);
230     }
231     else {
232         data->state = LV_INDEV_STATE_RELEASED;
233     }
234 }
235 
x11_mouse_read_cb(lv_indev_t * indev,lv_indev_data_t * data)236 static void x11_mouse_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
237 {
238     lv_display_t * disp = lv_indev_get_driver_data(indev);
239     x11_inp_data_t * xd = x11_input_get_user_data(disp);
240 
241     int32_t hor_res = lv_display_get_horizontal_resolution(disp);
242     int32_t ver_res = lv_display_get_vertical_resolution(disp);
243 
244     xd->mouse_pos.x = MIN(xd->mouse_pos.x, hor_res - 1);
245     xd->mouse_pos.y = MIN(xd->mouse_pos.y, ver_res - 1);
246 
247     data->point = xd->mouse_pos;
248     data->state = xd->left_mouse_btn ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
249 }
250 
x11_mousewheel_read_cb(lv_indev_t * indev,lv_indev_data_t * data)251 static void x11_mousewheel_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
252 {
253     lv_display_t * disp = lv_indev_get_driver_data(indev);
254     x11_inp_data_t * xd = x11_input_get_user_data(disp);
255 
256     data->state    = xd->wheel_mouse_btn ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
257     data->enc_diff = xd->wheel_cnt;
258     xd->wheel_cnt  = 0;
259 }
260 
lv_x11_keyboard_create(lv_display_t * disp)261 static lv_indev_t * lv_x11_keyboard_create(lv_display_t * disp)
262 {
263     lv_indev_t * indev = lv_indev_create();
264     LV_ASSERT_NULL(indev);
265     if(NULL != indev) {
266         lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);
267         lv_indev_set_read_cb(indev, x11_keyboard_read_cb);
268         lv_indev_set_driver_data(indev, disp);
269     }
270     return indev;
271 }
272 
lv_x11_mouse_create(lv_display_t * disp,lv_image_dsc_t const * symb)273 static lv_indev_t * lv_x11_mouse_create(lv_display_t * disp, lv_image_dsc_t const * symb)
274 {
275     lv_indev_t * indev = lv_indev_create();
276     if(NULL != indev) {
277         lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
278         lv_indev_set_read_cb(indev, x11_mouse_read_cb);
279         lv_indev_set_driver_data(indev, disp);
280 
281         /* optional mouse cursor symbol */
282         if(NULL != symb) {
283             lv_obj_t * mouse_cursor = lv_image_create(lv_screen_active());
284             lv_image_set_src(mouse_cursor, symb);
285             lv_indev_set_cursor(indev, mouse_cursor);
286         }
287     }
288     return indev;
289 }
290 
lv_x11_mousewheel_create(lv_display_t * disp)291 static lv_indev_t * lv_x11_mousewheel_create(lv_display_t * disp)
292 {
293     lv_indev_t * indev = lv_indev_create();
294     if(NULL != indev) {
295         lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER);
296         lv_indev_set_read_cb(indev, x11_mousewheel_read_cb);
297         lv_indev_set_driver_data(indev, disp);
298     }
299     return indev;
300 }
301 
302 /**********************
303  *   GLOBAL FUNCTIONS
304  **********************/
305 
lv_x11_inputs_create(lv_display_t * disp,lv_image_dsc_t const * mouse_img)306 void lv_x11_inputs_create(lv_display_t * disp, lv_image_dsc_t const * mouse_img)
307 {
308     x11_inp_data_t * xd = x11_input_get_user_data(disp);
309     LV_ASSERT_NULL(xd);
310 
311     xd->inp_group = lv_group_create();
312     lv_group_set_default(xd->inp_group);
313 
314     xd->mousepointer = lv_x11_mouse_create(disp, mouse_img);
315     lv_indev_set_group(xd->mousepointer, xd->inp_group);
316 
317     xd->mousewheel = lv_x11_mousewheel_create(disp);
318     lv_indev_set_group(xd->mousewheel, xd->inp_group);
319 
320     xd->keyboard = lv_x11_keyboard_create(disp);
321     lv_indev_set_group(xd->keyboard, xd->inp_group);
322 }
323 
324 #endif /*LV_USE_X11*/
325