1 #include "e.h"
2 
3 static void _e_xkb_update_event(int);
4 
5 static void _e_xkb_type_reconfig(E_Pixmap_Type comp_type);
6 static void _e_xkb_type_update(E_Pixmap_Type comp_type, int cur_group);
7 
8 static int _e_xkb_cur_group = -1;
9 
10 static Ecore_Exe *cur_exe;
11 
12 #ifndef HAVE_WAYLAND_ONLY
13 static Ecore_Event_Handler *xkb_state_handler = NULL;
14 static Ecore_Event_Handler *xkb_new_keyboard_handler = NULL;
15 static Ecore_Event_Handler *xkb_keymap_handler = NULL;
16 static int skip_new_keyboard = 0;
17 static Ecore_Timer *save_group = NULL;
18 #endif
19 
20 E_API int E_EVENT_XKB_CHANGED = 0;
21 
22 static void
_eval_cur_group(void)23 _eval_cur_group(void)
24 {
25    Eina_List *l;
26    E_Config_XKB_Layout *cl2, *cl = NULL;
27    int cur_group = -1;
28 
29    cl = e_config->xkb.current_layout;
30    if (!cl) cl = e_config->xkb.sel_layout;
31 
32    EINA_LIST_FOREACH(e_config->xkb.used_layouts, l, cl2)
33      {
34         cur_group++;
35         if (!cl2->name) continue;
36         if (!cl || e_config_xkb_layout_eq(cl, cl2))
37           {
38              INF("Setting keyboard layout: %s|%s|%s", cl2->name, cl2->model, cl2->variant);
39              e_config->xkb.cur_group = cur_group;
40              return;
41           }
42      }
43    e_config->xkb.cur_group = 0;
44 }
45 
46 #ifndef HAVE_WAYLAND_ONLY
47 static Eina_Bool
_e_xkb_init_timer(void * data EINA_UNUSED)48 _e_xkb_init_timer(void *data EINA_UNUSED)
49 {
50    if (!e_comp->root) return EINA_FALSE;
51 
52    _eval_cur_group();
53 
54    e_xkb_update(e_config->xkb.cur_group);
55    ecore_x_xkb_track_state();
56    return EINA_FALSE;
57 }
58 #endif
59 
60 #ifndef HAVE_WAYLAND_ONLY
61 
62 static Eina_Bool
_e_xkb_save_group(void * data)63 _e_xkb_save_group(void *data)
64 {
65    int group = (intptr_t)data;
66 
67    if (e_config->xkb.cur_group != group)
68      {
69         e_config->xkb.cur_group = group;
70         e_config_save_queue();
71         e_xkb_update(e_config->xkb.cur_group);
72      }
73    save_group = NULL;
74    return EINA_FALSE;
75 }
76 
77 static Eina_Bool
_xkb_new_keyboard(void * data EINA_UNUSED,int type EINA_UNUSED,void * event EINA_UNUSED)78 _xkb_new_keyboard(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
79 {
80    if (skip_new_keyboard > 0)
81      {
82         skip_new_keyboard --;
83         return ECORE_CALLBACK_PASS_ON;
84      }
85 
86    //we have to restore our settings here
87    e_xkb_reconfig();
88    e_xkb_update(e_config->xkb.cur_group);
89 
90    return ECORE_CALLBACK_PASS_ON;
91 }
92 
93 static Eina_Bool
_xkb_new_state(void * data EINA_UNUSED,int type EINA_UNUSED,void * event)94 _xkb_new_state(void* data EINA_UNUSED, int type EINA_UNUSED, void *event)
95 {
96    Ecore_X_Event_Xkb *ev = event;
97 
98    if (save_group) ecore_timer_del(save_group);
99    save_group = ecore_timer_loop_add(0.5, _e_xkb_save_group, (void *)(intptr_t)ev->group);
100 
101    return ECORE_CALLBACK_PASS_ON;
102 }
103 
104 static Eina_Bool
_xkb_keymap(void * data EINA_UNUSED,int type EINA_UNUSED,void * event EINA_UNUSED)105 _xkb_keymap(void* data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
106 {
107    return ECORE_CALLBACK_PASS_ON;
108 }
109 #endif
110 
111 static Eina_Bool
kb_exe_del(void * d EINA_UNUSED,int t EINA_UNUSED,Ecore_Exe_Event_Del * ev)112 kb_exe_del(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Exe_Event_Del *ev)
113 {
114    if (ev->exe == cur_exe)
115      cur_exe = NULL;
116    return ECORE_CALLBACK_RENEW;
117 }
118 
119 /* externally accessible functions */
120 E_API int
e_xkb_init(E_Pixmap_Type comp_type)121 e_xkb_init(E_Pixmap_Type comp_type)
122 {
123    if (!E_EVENT_XKB_CHANGED)
124      {
125         E_EVENT_XKB_CHANGED = ecore_event_type_new();
126         ecore_event_handler_add(ECORE_EXE_EVENT_DEL, (Ecore_Event_Handler_Cb)kb_exe_del, NULL);
127      }
128 #ifndef HAVE_WAYLAND_ONLY
129    if (comp_type == E_PIXMAP_TYPE_X)
130      {
131         xkb_state_handler = ecore_event_handler_add(ECORE_X_EVENT_XKB_STATE_NOTIFY, _xkb_new_state, NULL);
132         xkb_new_keyboard_handler = ecore_event_handler_add(ECORE_X_EVENT_XKB_NEWKBD_NOTIFY, _xkb_new_keyboard, NULL);
133         xkb_keymap_handler = ecore_event_handler_add(ECORE_X_EVENT_WINDOW_KEYMAP, _xkb_keymap, NULL);
134      }
135 #endif
136    if (e_config->xkb.dont_touch_my_damn_keyboard) return 1;
137 
138    _e_xkb_type_reconfig(comp_type);
139 
140 #ifndef HAVE_WAYLAND_ONLY
141    if (comp_type == E_PIXMAP_TYPE_X)
142      ecore_timer_loop_add(1.5, _e_xkb_init_timer, NULL);
143    else
144 #endif
145    if (comp_type == E_PIXMAP_TYPE_WL)
146      {
147         _eval_cur_group();
148         _e_xkb_type_update(comp_type, e_config->xkb.cur_group);
149      }
150 
151    return 1;
152 }
153 
154 E_API int
e_xkb_shutdown(void)155 e_xkb_shutdown(void)
156 {
157 #ifndef HAVE_WAYLAND_ONLY
158    E_FREE_FUNC(xkb_keymap_handler, ecore_event_handler_del);
159    E_FREE_FUNC(xkb_state_handler, ecore_event_handler_del);
160    E_FREE_FUNC(xkb_new_keyboard_handler, ecore_event_handler_del);
161    ecore_timer_del(save_group);
162    save_group = NULL;
163 #endif
164    return 1;
165 }
166 
167 static void
_e_x_xkb_reconfig(void)168 _e_x_xkb_reconfig(void)
169 {
170    E_Config_XKB_Layout *cl;
171    E_Config_XKB_Option *op;
172    Eina_List *l;
173    Eina_Strbuf *buf;
174 
175    if (e_config->xkb.dont_touch_my_damn_keyboard) return;
176    if ((!e_config->xkb.used_layouts) && (!e_config->xkb.used_options) && (!e_config->xkb.default_model)) return;
177    if (!getenv("DISPLAY")) return;
178 
179    buf = eina_strbuf_new();
180    eina_strbuf_append(buf, "setxkbmap ");
181 
182    if (e_config->xkb.used_layouts)
183      {
184         eina_strbuf_append(buf, "-layout '");
185         EINA_LIST_FOREACH(e_config->xkb.used_layouts, l, cl)
186           {
187              if (cl->name)
188                {
189                   eina_strbuf_append(buf, cl->name);
190                   eina_strbuf_append(buf, ",");
191                }
192           }
193 
194         eina_strbuf_append(buf, "' -variant '");
195         EINA_LIST_FOREACH(e_config->xkb.used_layouts, l, cl)
196           {
197              if ((cl->variant) && (strcmp(cl->variant, "basic")))
198                {
199                   eina_strbuf_append(buf, cl->variant);
200                   eina_strbuf_append(buf, ",");
201                }
202              else
203                eina_strbuf_append(buf, ",");
204           }
205         eina_strbuf_append(buf, "'");
206 
207         /* use first entry in used layouts */
208         cl = e_config->xkb.used_layouts->data;
209 
210         if (cl->model)
211           {
212              eina_strbuf_append(buf, " -model '");
213              if (strcmp(cl->model, "default"))
214                eina_strbuf_append(buf, cl->model);
215              else if ((e_config->xkb.default_model) &&
216                       (strcmp(e_config->xkb.default_model, "default")))
217                eina_strbuf_append(buf, e_config->xkb.default_model);
218              else
219                eina_strbuf_append(buf, "default");
220              eina_strbuf_append(buf, "'");
221           }
222      }
223    else if (e_config->xkb.default_model)
224      {
225         eina_strbuf_append(buf, " -model '");
226         eina_strbuf_append(buf, e_config->xkb.default_model);
227         eina_strbuf_append(buf, "'");
228      }
229 
230    if (e_config->xkb.used_options)
231      {
232         /* clear options */
233         eina_strbuf_append(buf, " -option ");
234 
235         /* add in selected options */
236         EINA_LIST_FOREACH(e_config->xkb.used_options, l, op)
237           {
238              if (op->name)
239                {
240                   eina_strbuf_append(buf, " -option '");
241                   eina_strbuf_append(buf, op->name);
242                   eina_strbuf_append(buf, "'");
243                }
244           }
245      }
246 #ifndef HAVE_WAYLAND_ONLY
247    skip_new_keyboard ++;
248 #endif
249    INF("SET XKB RUN: %s", eina_strbuf_string_get(buf));
250    E_FREE_FUNC(cur_exe, ecore_exe_kill);
251    cur_exe = ecore_exe_run(eina_strbuf_string_get(buf), NULL);
252    eina_strbuf_free(buf);
253 }
254 
255 static void
_e_x_xkb_update(int cur_group)256 _e_x_xkb_update(int cur_group)
257 {
258    if (e_config->xkb.dont_touch_my_damn_keyboard) return;
259    if ((!e_config->xkb.used_layouts) && (!e_config->xkb.used_options) && (!e_config->xkb.default_model)) return;
260    if (!getenv("DISPLAY")) return;
261    if (cur_group != _e_xkb_cur_group)
262      {
263         _e_xkb_cur_group = cur_group;
264 #ifndef HAVE_WAYLAND_ONLY
265         if (e_comp->root)
266           {
267              e_comp_canvas_keys_ungrab();
268              ecore_x_xkb_select_group(cur_group);
269              e_comp_canvas_keys_grab();
270           }
271 #endif
272         e_deskenv_xmodmap_run();
273         _e_xkb_update_event(cur_group);
274         return;
275      }
276 }
277 
278 
279 static void
_e_wl_xkb_update(int cur_group)280 _e_wl_xkb_update(int cur_group)
281 {
282 #ifdef HAVE_WAYLAND
283    e_comp_wl_input_keymap_index_set(cur_group);
284    _e_xkb_update_event(cur_group);
285 #else
286    (void)cur_group;
287 #endif
288 }
289 
290 
291 static void
_e_wl_xkb_reconfig(void)292 _e_wl_xkb_reconfig(void)
293 {
294 #ifdef HAVE_WAYLAND
295    E_Config_XKB_Option *op;
296    E_Config_XKB_Layout *cl;
297    Eina_Strbuf *options, *layouts, *variants;
298    Eina_List *l;
299 
300 
301    options = eina_strbuf_new();
302 
303     /* create options */
304    EINA_LIST_FOREACH(e_config->xkb.used_options, l, op)
305      {
306         if (op->name)
307           {
308              eina_strbuf_append(options, op->name);
309              eina_strbuf_append_char(options, ',');
310           }
311      }
312 
313    layouts = eina_strbuf_new();
314    variants = eina_strbuf_new();
315 
316    //search for the correct layout
317 
318    /* create layouts */
319    EINA_LIST_FOREACH(e_config->xkb.used_layouts, l, cl)
320      {
321         if (cl->name)
322           {
323              eina_strbuf_append(layouts, cl->name);
324              eina_strbuf_append_char(layouts, ',');
325           }
326         else
327           eina_strbuf_append_char(layouts, ',');
328 
329         if ((cl->variant) && (strcmp(cl->variant, "basic")))
330           {
331              eina_strbuf_append(variants, cl->variant);
332              eina_strbuf_append_char(variants, ',');
333           }
334         else
335           eina_strbuf_append_char(variants, ',');
336      }
337 
338    /* collect model to use */
339 
340    /* set keymap to the compositor */
341    e_comp_wl_input_keymap_set(NULL,
342                               e_config->xkb.default_model,
343                               eina_strbuf_string_get(layouts), //pool of layouts to use
344                               eina_strbuf_string_get(variants),  //pool of variants to use
345                               eina_strbuf_string_get(options) //list of options
346                              );
347 
348    eina_strbuf_free(variants);
349    eina_strbuf_free(layouts);
350    eina_strbuf_free(options);
351 #endif
352 }
353 
354 static void
_e_xkb_type_reconfig(E_Pixmap_Type comp_type)355 _e_xkb_type_reconfig(E_Pixmap_Type comp_type)
356 {
357    if (comp_type == E_PIXMAP_TYPE_X)
358      _e_x_xkb_reconfig();
359    else if (comp_type == E_PIXMAP_TYPE_WL)
360      _e_wl_xkb_reconfig();
361 }
362 
363 E_API void
e_xkb_reconfig(void)364 e_xkb_reconfig(void)
365 {
366    _e_xkb_type_reconfig(e_comp->comp_type);
367 }
368 
369 static void
_e_xkb_type_update(E_Pixmap_Type comp_type,int cur_group)370 _e_xkb_type_update(E_Pixmap_Type comp_type, int cur_group)
371 {
372    e_config->xkb.cur_group = cur_group;
373    e_config_save_queue();
374 
375    if (comp_type == E_PIXMAP_TYPE_WL)
376      _e_wl_xkb_update(cur_group);
377    else
378      _e_x_xkb_update(cur_group);
379 }
380 
381 E_API void
e_xkb_update(int cur_group)382 e_xkb_update(int cur_group)
383 {
384    _e_xkb_type_update(e_comp->comp_type, cur_group);
385 }
386 
387 E_API void
e_xkb_layout_next(void)388 e_xkb_layout_next(void)
389 {
390    Eina_List *l;
391    E_Config_XKB_Layout *cl;
392 
393    if (e_config->xkb.dont_touch_my_damn_keyboard) return;
394    if (!e_config->xkb.used_layouts) return;
395    l = eina_list_nth_list(e_config->xkb.used_layouts, e_config->xkb.cur_group);
396    l = eina_list_next(l);
397    if (!l) l = e_config->xkb.used_layouts;
398 
399    e_config->xkb.cur_group = (e_config->xkb.cur_group + 1) % eina_list_count(e_config->xkb.used_layouts);
400    cl = eina_list_data_get(l);
401    eina_stringshare_replace(&e_config->xkb.cur_layout, cl->name);
402    eina_stringshare_replace(&e_config->xkb.selected_layout, cl->name);
403    INF("Setting keyboard layout: %s|%s|%s", cl->name, cl->model, cl->variant);
404    e_xkb_update(e_config->xkb.cur_group);
405    _e_xkb_update_event(e_config->xkb.cur_group);
406    e_config_save_queue();
407 }
408 
409 E_API void
e_xkb_layout_prev(void)410 e_xkb_layout_prev(void)
411 {
412    Eina_List *l;
413    E_Config_XKB_Layout *cl;
414 
415    if (e_config->xkb.dont_touch_my_damn_keyboard) return;
416    if (!e_config->xkb.used_layouts) return;
417    l = eina_list_nth_list(e_config->xkb.used_layouts, e_config->xkb.cur_group);
418    l = eina_list_prev(l);
419    if (!l) l = eina_list_last(e_config->xkb.used_layouts);
420 
421    e_config->xkb.cur_group = (e_config->xkb.cur_group == 0) ?
422      ((int)eina_list_count(e_config->xkb.used_layouts) - 1) : (e_config->xkb.cur_group - 1);
423    cl = eina_list_data_get(l);
424    eina_stringshare_replace(&e_config->xkb.cur_layout, cl->name);
425    eina_stringshare_replace(&e_config->xkb.selected_layout, cl->name);
426    INF("Setting keyboard layout: %s|%s|%s", cl->name, cl->model, cl->variant);
427    e_xkb_update(e_config->xkb.cur_group);
428    _e_xkb_update_event(e_config->xkb.cur_group);
429    e_config_save_queue();
430 }
431 
432 /* always use this function to get the current layout's name
433  * to ensure the most accurate results!!!
434  */
435 E_API E_Config_XKB_Layout *
e_xkb_layout_get(void)436 e_xkb_layout_get(void)
437 {
438    unsigned int n = 0;
439 
440    if (e_config->xkb.dont_touch_my_damn_keyboard) return NULL;
441    if (e_config->xkb.current_layout) return e_config->xkb.current_layout;
442    if (_e_xkb_cur_group >= 0)
443      n = _e_xkb_cur_group;
444    return eina_list_nth(e_config->xkb.used_layouts, n);
445 }
446 
447 E_API void
e_xkb_layout_set(const E_Config_XKB_Layout * cl)448 e_xkb_layout_set(const E_Config_XKB_Layout *cl)
449 {
450    Eina_List *l;
451    E_Config_XKB_Layout *cl2;
452    int cur_group = -1;
453 
454    EINA_SAFETY_ON_NULL_RETURN(cl);
455    if (e_config->xkb.dont_touch_my_damn_keyboard) return;
456    if (e_config_xkb_layout_eq(e_config->xkb.current_layout, cl)) return;
457    cl2 = e_config_xkb_layout_dup(e_config->xkb.current_layout);
458    e_config_xkb_layout_free(e_config->xkb.current_layout);
459    e_config->xkb.current_layout = cl2;
460    EINA_LIST_FOREACH(e_config->xkb.used_layouts, l, cl2)
461      {
462         cur_group++;
463         if (!cl2->name) continue;
464         if (e_config_xkb_layout_eq(cl, cl2))
465           {
466              INF("Setting keyboard layout: %s|%s|%s", cl2->name, cl2->model, cl2->variant);
467              eina_stringshare_replace(&e_config->xkb.cur_layout, cl->name);
468              eina_stringshare_replace(&e_config->xkb.selected_layout, cl->name);
469              e_xkb_update(cur_group);
470              break;
471           }
472      }
473    _e_xkb_update_event(e_config->xkb.cur_group);
474    e_config_save_queue();
475 }
476 
477 E_API const char *
e_xkb_layout_name_reduce(const char * name)478 e_xkb_layout_name_reduce(const char *name)
479 {
480    const char *s;
481 
482    if (!name) return NULL;
483    s = strchr(name, '/');
484    if (s) s++;
485    else s = name;
486    return s;
487 }
488 
489 E_API void
e_xkb_e_icon_flag_setup(Evas_Object * eicon,const char * name)490 e_xkb_e_icon_flag_setup(Evas_Object *eicon, const char *name)
491 {
492    int w, h;
493    char buf[PATH_MAX];
494 
495    e_xkb_flag_file_get(buf, sizeof(buf), name);
496    e_icon_preload_set(eicon, EINA_FALSE);
497    e_icon_file_set(eicon, buf);
498    e_icon_size_get(eicon, &w, &h);
499    evas_object_size_hint_aspect_set(eicon, EVAS_ASPECT_CONTROL_BOTH, w, h);
500 }
501 
502 E_API void
e_xkb_flag_file_get(char * buf,size_t bufsize,const char * name)503 e_xkb_flag_file_get(char *buf, size_t bufsize, const char *name)
504 {
505    name = e_xkb_layout_name_reduce(name);
506    snprintf(buf, bufsize, "%s/data/flags/%s_flag.png",
507             e_prefix_data_get(), name ? name : "unknown");
508    if (!ecore_file_exists(buf))
509      snprintf(buf, bufsize, "%s/data/flags/unknown_flag.png",
510               e_prefix_data_get());
511 }
512 
513 E_API Eina_Bool
e_config_xkb_layout_eq(const E_Config_XKB_Layout * a,const E_Config_XKB_Layout * b)514 e_config_xkb_layout_eq(const E_Config_XKB_Layout *a, const E_Config_XKB_Layout *b)
515 {
516    if (a == b) return EINA_TRUE;
517    if ((!a) || (!b)) return EINA_FALSE;
518    return ((a->name == b->name) && (a->model == b->model) && (a->variant == b->variant));
519 }
520 
521 E_API void
e_config_xkb_layout_free(E_Config_XKB_Layout * cl)522 e_config_xkb_layout_free(E_Config_XKB_Layout *cl)
523 {
524    if (!cl) return;
525 
526    eina_stringshare_del(cl->name);
527    eina_stringshare_del(cl->model);
528    eina_stringshare_del(cl->variant);
529    free(cl);
530 }
531 
532 E_API E_Config_XKB_Layout *
e_config_xkb_layout_dup(const E_Config_XKB_Layout * cl)533 e_config_xkb_layout_dup(const E_Config_XKB_Layout *cl)
534 {
535    E_Config_XKB_Layout *cl2;
536 
537    EINA_SAFETY_ON_NULL_RETURN_VAL(cl, NULL);
538    cl2 = E_NEW(E_Config_XKB_Layout, 1);
539    cl2->name = eina_stringshare_ref(cl->name);
540    cl2->model = eina_stringshare_ref(cl->model);
541    cl2->variant = eina_stringshare_ref(cl->variant);
542    return cl2;
543 }
544 
545 static void
_e_xkb_update_event(int cur_group)546 _e_xkb_update_event(int cur_group)
547 {
548    ecore_event_add(E_EVENT_XKB_CHANGED, NULL, NULL, (intptr_t *)(long)cur_group);
549 }
550 
551