1 /**
2  * @file lv_sdl_keyboard.c
3  *
4  */
5 
6 /*********************
7  *      INCLUDES
8  *********************/
9 #include "lv_sdl_keyboard.h"
10 #if LV_USE_SDL
11 
12 #include "../../core/lv_group.h"
13 #include "../../stdlib/lv_string.h"
14 #include "lv_sdl_private.h"
15 
16 /*********************
17  *      DEFINES
18  *********************/
19 
20 /**********************
21  *      TYPEDEFS
22  **********************/
23 typedef struct {
24     char buf[KEYBOARD_BUFFER_SIZE];
25     bool dummy_read;
26 } lv_sdl_keyboard_t;
27 
28 /**********************
29  *  STATIC PROTOTYPES
30  **********************/
31 static void sdl_keyboard_read(lv_indev_t * indev, lv_indev_data_t * data);
32 static uint32_t keycode_to_ctrl_key(SDL_Keycode sdl_key);
33 static void release_indev_cb(lv_event_t * e);
34 
35 /**********************
36  *  STATIC VARIABLES
37  **********************/
38 
39 /**********************
40  *   GLOBAL FUNCTIONS
41  **********************/
42 
lv_sdl_keyboard_create(void)43 lv_indev_t * lv_sdl_keyboard_create(void)
44 {
45     lv_sdl_keyboard_t * dsc = lv_malloc_zeroed(sizeof(lv_sdl_keyboard_t));
46     LV_ASSERT_MALLOC(dsc);
47     if(dsc == NULL) return NULL;
48 
49     lv_indev_t * indev = lv_indev_create();
50     LV_ASSERT_MALLOC(indev);
51     if(indev == NULL) {
52         lv_free(dsc);
53         return NULL;
54     }
55 
56     lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);
57     lv_indev_set_read_cb(indev, sdl_keyboard_read);
58     lv_indev_set_driver_data(indev, dsc);
59     lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT);
60     lv_indev_add_event_cb(indev, release_indev_cb, LV_EVENT_DELETE, indev);
61 
62     return indev;
63 }
64 
65 /**********************
66  *   STATIC FUNCTIONS
67  **********************/
68 
sdl_keyboard_read(lv_indev_t * indev,lv_indev_data_t * data)69 static void sdl_keyboard_read(lv_indev_t * indev, lv_indev_data_t * data)
70 {
71     lv_sdl_keyboard_t * dev = lv_indev_get_driver_data(indev);
72 
73     const size_t len = lv_strlen(dev->buf);
74 
75     /*Send a release manually*/
76     if(dev->dummy_read) {
77         dev->dummy_read = false;
78         data->state = LV_INDEV_STATE_RELEASED;
79     }
80     /*Send the pressed character*/
81     else if(len > 0) {
82         dev->dummy_read = true;
83         data->state = LV_INDEV_STATE_PRESSED;
84         data->key = dev->buf[0];
85         lv_memmove(dev->buf, dev->buf + 1, len);
86     }
87 }
88 
release_indev_cb(lv_event_t * e)89 static void release_indev_cb(lv_event_t * e)
90 {
91     lv_indev_t * indev = (lv_indev_t *) lv_event_get_user_data(e);
92     lv_sdl_keyboard_t * dev = lv_indev_get_driver_data(indev);
93     if(dev) {
94         lv_indev_set_driver_data(indev, NULL);
95         lv_indev_set_read_cb(indev, NULL);
96         lv_free(dev);
97         LV_LOG_INFO("done");
98     }
99 }
100 
lv_sdl_keyboard_handler(SDL_Event * event)101 void lv_sdl_keyboard_handler(SDL_Event * event)
102 {
103     uint32_t win_id = UINT32_MAX;
104     switch(event->type) {
105         case SDL_KEYDOWN:
106             win_id = event->key.windowID;
107             break;
108         case SDL_TEXTINPUT:
109             win_id = event->text.windowID;
110             break;
111         default:
112             return;
113     }
114 
115     lv_display_t * disp = lv_sdl_get_disp_from_win_id(win_id);
116 
117 
118     /*Find a suitable indev*/
119     lv_indev_t * indev = lv_indev_get_next(NULL);
120     while(indev) {
121         if(lv_indev_get_read_cb(indev) == sdl_keyboard_read) {
122             /*If disp is NULL for any reason use the first indev with the correct type*/
123             if(disp == NULL || lv_indev_get_display(indev) == disp) break;
124         }
125         indev = lv_indev_get_next(indev);
126     }
127     if(indev == NULL) return;
128     lv_sdl_keyboard_t * dsc = lv_indev_get_driver_data(indev);
129 
130     /* We only care about SDL_KEYDOWN and SDL_TEXTINPUT events */
131     switch(event->type) {
132         case SDL_KEYDOWN: {                     /*Button press*/
133                 const uint32_t ctrl_key = keycode_to_ctrl_key(event->key.keysym.sym);
134                 if(ctrl_key == '\0')
135                     return;
136                 const size_t len = lv_strlen(dsc->buf);
137                 if(len < KEYBOARD_BUFFER_SIZE - 1) {
138                     dsc->buf[len] = ctrl_key;
139                     dsc->buf[len + 1] = '\0';
140                 }
141                 break;
142             }
143         case SDL_TEXTINPUT: {                   /*Text input*/
144                 const size_t len = lv_strlen(dsc->buf) + lv_strlen(event->text.text);
145                 if(len < KEYBOARD_BUFFER_SIZE - 1)
146                     strcat(dsc->buf, event->text.text);
147             }
148             break;
149         default:
150             break;
151 
152     }
153 
154     size_t len = lv_strlen(dsc->buf);
155     while(len) {
156         lv_indev_read(indev);
157 
158         /*Call again to handle dummy read in `sdl_keyboard_read`*/
159         lv_indev_read(indev);
160         len--;
161     }
162 }
163 
164 /**
165  * Convert a SDL key code to it's LV_KEY_* counterpart or return '\0' if it's not a control character.
166  * @param sdl_key the key code
167  * @return LV_KEY_* control character or '\0'
168  */
keycode_to_ctrl_key(SDL_Keycode sdl_key)169 static uint32_t keycode_to_ctrl_key(SDL_Keycode sdl_key)
170 {
171     /*Remap some key to LV_KEY_... to manage groups*/
172     switch(sdl_key) {
173         case SDLK_RIGHT:
174         case SDLK_KP_PLUS:
175             return LV_KEY_RIGHT;
176 
177         case SDLK_LEFT:
178         case SDLK_KP_MINUS:
179             return LV_KEY_LEFT;
180 
181         case SDLK_UP:
182             return LV_KEY_UP;
183 
184         case SDLK_DOWN:
185             return LV_KEY_DOWN;
186 
187         case SDLK_ESCAPE:
188             return LV_KEY_ESC;
189 
190         case SDLK_BACKSPACE:
191             return LV_KEY_BACKSPACE;
192 
193         case SDLK_DELETE:
194             return LV_KEY_DEL;
195 
196         case SDLK_KP_ENTER:
197         case '\r':
198             return LV_KEY_ENTER;
199 
200         case SDLK_TAB:
201         case SDLK_PAGEDOWN:
202             return LV_KEY_NEXT;
203 
204         case SDLK_PAGEUP:
205             return LV_KEY_PREV;
206 
207         case SDLK_HOME:
208             return LV_KEY_HOME;
209 
210         case SDLK_END:
211             return LV_KEY_END;
212 
213         default:
214             return '\0';
215     }
216 }
217 
218 #endif /*LV_USE_SDL*/
219