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