1 /**
2  * @file lv_nuttx_touchscreen.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 
10 #include "lv_nuttx_touchscreen.h"
11 
12 #if LV_USE_NUTTX
13 
14 #if LV_USE_NUTTX_TOUCHSCREEN
15 
16 #include <sys/types.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <debug.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <nuttx/input/touchscreen.h>
24 #include "../../lvgl_private.h"
25 
26 /*********************
27  *      DEFINES
28  *********************/
29 
30 /**********************
31  *      TYPEDEFS
32  **********************/
33 
34 typedef struct {
35     /* fd should be defined at the beginning */
36     int fd;
37     struct touch_sample_s last_sample;
38     bool has_last_sample;
39     lv_indev_state_t last_state;
40     lv_indev_t * indev_drv;
41 } lv_nuttx_touchscreen_t;
42 
43 /**********************
44  *  STATIC PROTOTYPES
45  **********************/
46 static void indev_set_cursor(lv_indev_t * indev, int32_t size);
47 static void touchscreen_read(lv_indev_t * drv, lv_indev_data_t * data);
48 static void touchscreen_delete_cb(lv_event_t * e);
49 static lv_indev_t * touchscreen_init(int fd);
50 
51 /**********************
52  *  STATIC VARIABLES
53  **********************/
54 
55 /**********************
56  *      MACROS
57  **********************/
58 
59 /**********************
60  *   GLOBAL FUNCTIONS
61  **********************/
62 
lv_nuttx_touchscreen_create(const char * dev_path)63 lv_indev_t * lv_nuttx_touchscreen_create(const char * dev_path)
64 {
65     lv_indev_t * indev;
66     int fd;
67 
68     LV_ASSERT_NULL(dev_path);
69     LV_LOG_USER("touchscreen %s opening", dev_path);
70     fd = open(dev_path, O_RDONLY | O_NONBLOCK);
71     if(fd < 0) {
72         perror("Error: cannot open touchscreen device");
73         return NULL;
74     }
75 
76     LV_LOG_USER("touchscreen %s open success", dev_path);
77 
78     indev = touchscreen_init(fd);
79 
80     if(indev == NULL) {
81         close(fd);
82     }
83 
84     indev_set_cursor(indev, LV_NUTTX_TOUCHSCREEN_CURSOR_SIZE);
85 
86     return indev;
87 }
88 
89 /**********************
90  *   STATIC FUNCTIONS
91  **********************/
92 
indev_set_cursor(lv_indev_t * indev,int32_t size)93 static void indev_set_cursor(lv_indev_t * indev, int32_t size)
94 {
95     lv_obj_t * cursor_obj = lv_indev_get_cursor(indev);
96     if(size <= 0) {
97         if(cursor_obj) {
98             lv_obj_delete(cursor_obj);
99             lv_indev_set_cursor(indev, NULL);
100         }
101     }
102     else {
103         if(cursor_obj == NULL) {
104             cursor_obj = lv_obj_create(lv_layer_sys());
105             lv_obj_remove_style_all(cursor_obj);
106             lv_obj_set_style_radius(cursor_obj, LV_RADIUS_CIRCLE, 0);
107             lv_obj_set_style_bg_opa(cursor_obj, LV_OPA_50, 0);
108             lv_obj_set_style_bg_color(cursor_obj, lv_color_black(), 0);
109             lv_obj_set_style_border_width(cursor_obj, 2, 0);
110             lv_obj_set_style_border_color(cursor_obj, lv_palette_main(LV_PALETTE_GREY), 0);
111         }
112         lv_obj_set_size(cursor_obj, size, size);
113         lv_obj_set_style_translate_x(cursor_obj, -size / 2, 0);
114         lv_obj_set_style_translate_y(cursor_obj, -size / 2, 0);
115         lv_indev_set_cursor(indev, cursor_obj);
116     }
117 }
118 
conv_touch_sample(lv_indev_t * drv,lv_indev_data_t * data,struct touch_sample_s * sample)119 static void conv_touch_sample(lv_indev_t * drv,
120                               lv_indev_data_t * data,
121                               struct touch_sample_s * sample)
122 {
123     lv_nuttx_touchscreen_t * touchscreen = drv->driver_data;
124     uint8_t touch_flags = sample->point[0].flags;
125 
126     if(touch_flags & (TOUCH_DOWN | TOUCH_MOVE)) {
127         lv_display_t * disp = lv_indev_get_display(drv);
128         int32_t hor_max = lv_display_get_horizontal_resolution(disp) - 1;
129         int32_t ver_max = lv_display_get_vertical_resolution(disp) - 1;
130 
131         data->point.x = LV_CLAMP(0, sample->point[0].x, hor_max);
132         data->point.y = LV_CLAMP(0, sample->point[0].y, ver_max);
133         touchscreen->last_state = LV_INDEV_STATE_PRESSED;
134     }
135     else if(touch_flags & TOUCH_UP) {
136         touchscreen->last_state = LV_INDEV_STATE_RELEASED;
137     }
138 }
139 
touchscreen_read_sample(int fd,struct touch_sample_s * sample)140 static bool touchscreen_read_sample(int fd, struct touch_sample_s * sample)
141 {
142     int nbytes = read(fd, sample, sizeof(struct touch_sample_s));
143     return nbytes == sizeof(struct touch_sample_s);
144 }
145 
touchscreen_read(lv_indev_t * drv,lv_indev_data_t * data)146 static void touchscreen_read(lv_indev_t * drv, lv_indev_data_t * data)
147 {
148     lv_nuttx_touchscreen_t * touchscreen = drv->driver_data;
149     struct touch_sample_s sample;
150 
151     /*
152      * Note: Since it is necessary to avoid multi-processing click events
153      * caused by redundant continue_reading, a two-unit sample sliding window
154      * algorithm is used here. continue_reading is only activated when there
155      * are two points in the window.
156      */
157 
158     /* If has last sample, use it first */
159     if(touchscreen->has_last_sample) {
160         conv_touch_sample(drv, data, &touchscreen->last_sample);
161     }
162     else {
163         /* Read first sample */
164         if(!touchscreen_read_sample(touchscreen->fd, &sample)) {
165             /* No sample available, return last state */
166             data->state = touchscreen->last_state;
167             return;
168         }
169 
170         conv_touch_sample(drv, data, &sample);
171     }
172 
173     /* Try to read next sample */
174     if(touchscreen_read_sample(touchscreen->fd, &sample)) {
175         /* Save last sample and let lvgl continue reading */
176         touchscreen->last_sample = sample;
177         touchscreen->has_last_sample = true;
178         data->continue_reading = true;
179     }
180     else {
181         /* No more sample available, clear last sample flag */
182         touchscreen->has_last_sample = false;
183     }
184 
185     data->state = touchscreen->last_state;
186 }
187 
touchscreen_delete_cb(lv_event_t * e)188 static void touchscreen_delete_cb(lv_event_t * e)
189 {
190     lv_indev_t * indev = (lv_indev_t *) lv_event_get_user_data(e);
191     lv_nuttx_touchscreen_t * touchscreen = lv_indev_get_driver_data(indev);
192     if(touchscreen) {
193         lv_indev_set_driver_data(indev, NULL);
194         lv_indev_set_read_cb(indev, NULL);
195         indev_set_cursor(indev, -1);
196         if(touchscreen->fd >= 0) {
197             close(touchscreen->fd);
198             touchscreen->fd = -1;
199         }
200         lv_free(touchscreen);
201         LV_LOG_USER("done");
202     }
203 }
204 
touchscreen_init(int fd)205 static lv_indev_t * touchscreen_init(int fd)
206 {
207     lv_nuttx_touchscreen_t * touchscreen;
208     lv_indev_t * indev = NULL;
209 
210     touchscreen = lv_malloc_zeroed(sizeof(lv_nuttx_touchscreen_t));
211     if(touchscreen == NULL) {
212         LV_LOG_ERROR("touchscreen_s malloc failed");
213         return NULL;
214     }
215 
216     touchscreen->fd = fd;
217     touchscreen->last_state = LV_INDEV_STATE_RELEASED;
218     touchscreen->indev_drv = indev = lv_indev_create();
219 
220     lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
221     lv_indev_set_read_cb(indev, touchscreen_read);
222     lv_indev_set_driver_data(indev, touchscreen);
223     lv_indev_add_event_cb(indev, touchscreen_delete_cb, LV_EVENT_DELETE, indev);
224     return indev;
225 }
226 
227 #endif /*LV_USE_NUTTX_TOUCHSCREEN*/
228 
229 #endif /* LV_USE_NUTTX*/
230