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