1 /* TODO
2 check http://www.pvv.org/~mariusbu/proposal.html
3 for advances in cross toolkit settings */
4
5 #include <e.h>
6 //#include <X11/Xlib.h>
7 //#include <X11/Xmd.h> /* For CARD16 */
8
9 // define here to avoid needing x includes directly.
10 #define C16 unsigned short
11 #define C32 unsigned int
12
13 #define RETRY_TIMEOUT 2.0
14
15 #define SETTING_TYPE_INT 0
16 #define SETTING_TYPE_STRING 1
17 #define SETTING_TYPE_COLOR 2
18
19 #define OFFSET_ADD(n) ((n + 4 - 1) & (~(4 - 1)))
20
21 typedef struct _Settings_Manager Settings_Manager;
22 typedef struct _Setting Setting;
23
24 struct _Settings_Manager
25 {
26 Ecore_X_Window selection;
27 Ecore_Timer *timer_retry;
28 unsigned long serial;
29 Ecore_X_Atom _atom_xsettings_screen;
30 Eina_Bool enabled E_BITFIELD;
31 };
32
33 struct _Setting
34 {
35 unsigned short type;
36
37 const char *name;
38
39 struct
40 {
41 const char *value;
42 } s;
43 struct
44 {
45 int value;
46 } i;
47 struct
48 {
49 unsigned short red, green, blue, alpha;
50 } c;
51
52 unsigned long length;
53 unsigned long last_change;
54 };
55
56 static void _e_xsettings_apply(Settings_Manager *sm);
57
58 static Ecore_X_Atom _atom_manager = 0;
59 static Ecore_X_Atom _atom_xsettings = 0;
60 static Ecore_X_Atom _atom_gtk_iconthemes = 0;
61 static Ecore_X_Atom _atom_gtk_rcfiles = 0;
62 static Settings_Manager *manager = NULL;
63 static Eina_List *settings = NULL;
64 static Eina_Bool running = EINA_FALSE;
65 static Eio_File *eio_op = NULL;
66 static Eina_Bool setting = EINA_FALSE;
67 static Eina_Bool reset = EINA_FALSE;
68 static const char _setting_icon_theme_name[] = "Net/IconThemeName";
69 static const char _setting_theme_name[] = "Net/ThemeName";
70 static const char _setting_font_name[] = "Gtk/FontName";
71 #if 0
72 static const char _setting_xft_dpi[] = "Xft/DPI";
73 #endif
74 static const char *_setting_theme = NULL;
75
76 static void _e_xsettings_done_cb(void *data, Eio_File *handler, const Eina_Stat *stat);
77
78 static Ecore_X_Atom
_e_xsettings_atom_screen_get(int screen_num)79 _e_xsettings_atom_screen_get(int screen_num)
80 {
81 char buf[32];
82
83 snprintf(buf, sizeof(buf), "_XSETTINGS_S%d", screen_num);
84 return ecore_x_atom_get(buf);
85 }
86
87 static Eina_Bool
_e_xsettings_selection_owner_set(void)88 _e_xsettings_selection_owner_set(void)
89 {
90 Ecore_X_Atom atom;
91 Ecore_X_Window cur_selection;
92 Eina_Bool ret;
93
94 atom = _e_xsettings_atom_screen_get(0);
95 ecore_x_selection_owner_set(e_comp->cm_selection, atom,
96 ecore_x_current_time_get());
97 ecore_x_sync();
98 cur_selection = ecore_x_selection_owner_get(atom);
99
100 ret = (cur_selection == e_comp->cm_selection);
101 if (!ret)
102 ERR("XSETTINGS: tried to set selection to %#x, but got %#x",
103 (unsigned int)e_comp->cm_selection, cur_selection);
104
105 return ret;
106 }
107
108 static void
_e_xsettings_deactivate(Settings_Manager * sm)109 _e_xsettings_deactivate(Settings_Manager *sm)
110 {
111 Ecore_X_Atom atom;
112
113 atom = _e_xsettings_atom_screen_get(0);
114 ecore_x_selection_owner_set(0, atom, ecore_x_current_time_get());
115 ecore_x_sync();
116 sm->enabled = 0;
117 }
118
119 static Eina_Bool
_e_xsettings_activate(Settings_Manager * sm)120 _e_xsettings_activate(Settings_Manager *sm)
121 {
122 Ecore_X_Atom atom;
123 Ecore_X_Window old_win;
124
125 if (sm->enabled) return 1;
126
127 atom = _e_xsettings_atom_screen_get(0);
128 old_win = ecore_x_selection_owner_get(atom);
129 if (old_win != 0) return 0;
130
131 if (!_e_xsettings_selection_owner_set())
132 return 0;
133
134 ecore_x_client_message32_send(e_comp->root, _atom_manager,
135 ECORE_X_EVENT_MASK_WINDOW_CONFIGURE,
136 ecore_x_current_time_get(), atom,
137 e_comp->cm_selection, 0, 0);
138
139 if (settings) _e_xsettings_apply(sm);
140 sm->enabled = 1;
141
142 return 1;
143 }
144
145 static Eina_Bool
_e_xsettings_activate_retry(void * data)146 _e_xsettings_activate_retry(void *data)
147 {
148 Settings_Manager *sm = data;
149 Eina_Bool ret;
150
151 INF("XSETTINGS: reactivate...");
152 ret = _e_xsettings_activate(sm);
153 if (ret)
154 INF("XSETTINGS: activate success!");
155 else
156 ERR("XSETTINGS: activate failure! retrying in %0.1f seconds", RETRY_TIMEOUT);
157
158 if (!ret)
159 return ECORE_CALLBACK_RENEW;
160
161 sm->timer_retry = NULL;
162 return ECORE_CALLBACK_CANCEL;
163 }
164
165 static void
_e_xsettings_retry(Settings_Manager * sm)166 _e_xsettings_retry(Settings_Manager *sm)
167 {
168 if (sm->timer_retry) return;
169 sm->timer_retry = ecore_timer_loop_add
170 (RETRY_TIMEOUT, _e_xsettings_activate_retry, sm);
171 }
172
173 static void
_e_xsettings_string_set(const char * name,const char * value)174 _e_xsettings_string_set(const char *name, const char *value)
175 {
176 Setting *s;
177 Eina_List *l;
178
179 if (!name) return;
180 if (name == _setting_theme_name)
181 e_config->xsettings.net_theme_name_detected = value;
182 name = eina_stringshare_add(name);
183
184 EINA_LIST_FOREACH(settings, l, s)
185 {
186 if (s->type != SETTING_TYPE_STRING) continue;
187 if (s->name == name) break;
188 }
189 if (!value)
190 {
191 if (!s) return;
192 DBG("remove %s\n", name);
193 eina_stringshare_del(name);
194 eina_stringshare_del(s->name);
195 eina_stringshare_del(s->s.value);
196 settings = eina_list_remove(settings, s);
197 E_FREE(s);
198 return;
199 }
200 if (s)
201 {
202 DBG("update %s %s\n", name, value);
203 eina_stringshare_del(name);
204 eina_stringshare_replace(&s->s.value, value);
205 }
206 else
207 {
208 DBG("add %s %s\n", name, value);
209 s = E_NEW(Setting, 1);
210 s->type = SETTING_TYPE_STRING;
211 s->name = name;
212 s->s.value = eina_stringshare_add(value);
213 settings = eina_list_append(settings, s);
214 }
215
216 /* type + pad + name-len + last-change-serial + str_len */
217 s->length = 12;
218 s->length += OFFSET_ADD(strlen(name));
219 s->length += OFFSET_ADD(strlen(value));
220 s->last_change = ecore_x_current_time_get();
221 }
222
223 #if 0
224 static void
225 _e_xsettings_int_set(const char *name, int value, Eina_Bool set)
226 {
227 Setting *s;
228 Eina_List *l;
229
230 if (!name) return;
231 name = eina_stringshare_add(name);
232
233 EINA_LIST_FOREACH(settings, l, s)
234 {
235 if (s->type != SETTING_TYPE_INT) continue;
236 if (s->name == name) break;
237 }
238 if (!set)
239 {
240 if (!s) return;
241 DBG("remove %s\n", name);
242 eina_stringshare_del(name);
243 eina_stringshare_del(s->name);
244 settings = eina_list_remove(settings, s);
245 E_FREE(s);
246 return;
247 }
248 if (s)
249 {
250 DBG("update %s %d\n", name, value);
251 eina_stringshare_del(name);
252 s->i.value = value;
253 }
254 else
255 {
256 DBG("add %s %d\n", name, value);
257 s = E_NEW(Setting, 1);
258 s->type = SETTING_TYPE_INT;
259 s->name = name;
260 s->i.value = value;
261 settings = eina_list_append(settings, s);
262 }
263
264 // type + pad + name-len + last-change-serial + value
265 s->length = 12;
266 s->length += OFFSET_ADD(strlen(name));
267 }
268
269 #endif
270
271 static unsigned char *
_e_xsettings_copy(unsigned char * buffer,Setting * s)272 _e_xsettings_copy(unsigned char *buffer, Setting *s)
273 {
274 size_t str_len;
275 size_t len;
276 C16 tmp16;
277 C32 tmp32;
278
279 buffer[0] = s->type;
280 buffer[1] = 0;
281 buffer += 2;
282
283 str_len = strlen(s->name);
284 tmp16 = str_len;
285 memcpy(buffer, &tmp16, sizeof(C16));
286 buffer += 2;
287
288 memcpy(buffer, s->name, str_len);
289 buffer += str_len;
290
291 len = OFFSET_ADD(str_len) - str_len;
292 memset(buffer, 0, len);
293 buffer += len;
294
295 tmp32 = s->last_change;
296 memcpy(buffer, &tmp32, sizeof(C32));
297 buffer += 4;
298
299 switch (s->type)
300 {
301 case SETTING_TYPE_INT:
302 tmp32 = s->i.value;
303 memcpy(buffer, &tmp32, sizeof(C32));
304 buffer += 4;
305 break;
306
307 case SETTING_TYPE_STRING:
308 str_len = strlen(s->s.value);
309 tmp32 = str_len;
310 memcpy(buffer, &tmp32, sizeof(C32));
311 buffer += 4;
312
313 memcpy(buffer, s->s.value, str_len);
314 buffer += str_len;
315
316 len = OFFSET_ADD(str_len) - str_len;
317 memset(buffer, 0, len);
318 buffer += len;
319 break;
320
321 case SETTING_TYPE_COLOR:
322 tmp16 = s->c.red;
323 memcpy(buffer, &tmp16, sizeof(C16));
324 buffer += 2;
325 tmp16 = s->c.green;
326 memcpy(buffer, &tmp16, sizeof(C16));
327 buffer += 2;
328 tmp16 = s->c.blue;
329 memcpy(buffer, &tmp16, sizeof(C16));
330 buffer += 2;
331 tmp16 = s->c.alpha;
332 memcpy(buffer, &tmp16, sizeof(C16));
333 buffer += 2;
334 break;
335 }
336
337 return buffer;
338 }
339
340 static void
_e_xsettings_apply(Settings_Manager * sm)341 _e_xsettings_apply(Settings_Manager *sm)
342 {
343 unsigned char *data;
344 unsigned char *pos;
345 size_t len = 12;
346 Setting *s;
347 Eina_List *l;
348 C32 tmp32;
349
350 EINA_LIST_FOREACH(settings, l, s)
351 len += s->length;
352
353 pos = data = calloc(1, len);
354 if (!data) return;
355
356 #if (defined __BYTE_ORDER && __BYTE_ORDER == __LITTLE_ENDIAN) || (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
357 *pos = 0; //LSBFirst
358 #else
359 *pos = 1; //MSBFirst
360 #endif
361
362 pos += 4;
363 tmp32 = sm->serial++;
364 memcpy(pos, &tmp32, sizeof(C32));
365 pos += 4;
366 tmp32 = eina_list_count(settings);
367 memcpy(pos, &tmp32, sizeof(C32));
368 pos += 4;
369
370 EINA_LIST_FOREACH(settings, l, s)
371 pos = _e_xsettings_copy(pos, s);
372
373 ecore_x_window_prop_property_set(e_comp->cm_selection,
374 _atom_xsettings,
375 _atom_xsettings,
376 8, data, len);
377 free(data);
378 }
379
380 static void
_e_xsettings_update(void)381 _e_xsettings_update(void)
382 {
383 if (e_comp->cm_selection) _e_xsettings_apply(manager);
384 }
385
386 static void
_e_xsettings_gtk_icon_update(void)387 _e_xsettings_gtk_icon_update(void)
388 {
389 const Eina_List *l;
390 E_Client *ec;
391
392 EINA_LIST_FOREACH(e_comp->clients, l, ec)
393 if (ec->icccm.state)
394 ecore_x_client_message8_send(e_client_util_win_get(ec),
395 _atom_gtk_iconthemes, NULL, 0);
396 }
397
398 static void
_e_xsettings_gtk_rcfiles_update(void)399 _e_xsettings_gtk_rcfiles_update(void)
400 {
401 const Eina_List *l;
402 E_Client *ec;
403
404 EINA_LIST_FOREACH(e_comp->clients, l, ec)
405 if (ec->icccm.state)
406 ecore_x_client_message8_send(e_client_util_win_get(ec),
407 _atom_gtk_rcfiles, NULL, 0);
408 }
409
410 static void
_e_xsettings_icon_theme_set(void)411 _e_xsettings_icon_theme_set(void)
412 {
413 if (e_config->xsettings.match_e17_icon_theme)
414 {
415 _e_xsettings_string_set(_setting_icon_theme_name,
416 e_config->icon_theme);
417 return;
418 }
419
420 if (e_config->xsettings.net_icon_theme_name)
421 {
422 _e_xsettings_string_set(_setting_icon_theme_name,
423 e_config->xsettings.net_icon_theme_name);
424 return;
425 }
426
427 _e_xsettings_string_set(_setting_icon_theme_name, NULL);
428 }
429
430 static void
_e_xsettings_error_cb(void * data,Eio_File * handler EINA_UNUSED,int error EINA_UNUSED)431 _e_xsettings_error_cb(void *data, Eio_File *handler EINA_UNUSED, int error EINA_UNUSED)
432 {
433 Eina_List *l = data;
434 if (reset || setting)
435 {
436 char buf[PATH_MAX];
437 if (reset || (!l)) l = efreet_data_dirs_get();
438 else if (l)
439 l = l->next;
440 reset = EINA_FALSE;
441 if (l)
442 {
443 snprintf(buf, sizeof(buf), "%s/themes/%s",
444 (char *)eina_list_data_get(l), _setting_theme);
445 eio_op = eio_file_direct_stat(buf, _e_xsettings_done_cb,
446 _e_xsettings_error_cb, l);
447 return;
448 }
449 }
450 eio_op = NULL;
451 setting = EINA_FALSE;
452 _setting_theme = NULL;
453
454 if (e_config->xsettings.net_theme_name)
455 _e_xsettings_string_set(_setting_theme_name,
456 e_config->xsettings.net_theme_name);
457 else
458 _e_xsettings_string_set(_setting_theme_name, NULL);
459 _e_xsettings_update();
460 }
461
462 static void
_e_xsettings_done_cb(void * data EINA_UNUSED,Eio_File * handler EINA_UNUSED,const Eina_Stat * estat EINA_UNUSED)463 _e_xsettings_done_cb(void *data EINA_UNUSED, Eio_File *handler EINA_UNUSED, const Eina_Stat *estat EINA_UNUSED)
464 {
465 if (reset)
466 {
467 /* should not happen */
468 _e_xsettings_error_cb(NULL, NULL, 0);
469 return;
470 }
471 _e_xsettings_string_set(_setting_theme_name, _setting_theme);
472 _setting_theme = NULL;
473 eio_op = NULL;
474 setting = EINA_FALSE;
475 _e_xsettings_update();
476 }
477
478 static void
_e_xsettings_theme_set(void)479 _e_xsettings_theme_set(void)
480 {
481 if (e_config->xsettings.match_e17_theme)
482 {
483 const char *file;
484
485 file = e_theme_edje_file_get(NULL, "e/desktop/background");
486 if (file)
487 {
488 if ((_setting_theme = edje_file_data_get(file, "gtk-theme")))
489 {
490 char buf[PATH_MAX];
491
492 e_user_homedir_snprintf(buf, sizeof(buf),
493 ".themes/%s", _setting_theme);
494 eio_op = eio_file_direct_stat(buf, _e_xsettings_done_cb,
495 _e_xsettings_error_cb, NULL);
496 setting = EINA_TRUE;
497 return;
498 }
499 }
500 }
501
502 if (e_config->xsettings.net_theme_name)
503 {
504 _e_xsettings_string_set(_setting_theme_name,
505 e_config->xsettings.net_theme_name);
506 return;
507 }
508
509 _e_xsettings_string_set(_setting_theme_name, NULL);
510 }
511
512 static void
_e_xsettings_font_set(void)513 _e_xsettings_font_set(void)
514 {
515 E_Font_Default *efd;
516 E_Font_Properties *efp;
517
518 efd = e_font_default_get("application");
519
520 if (efd && efd->font)
521 {
522 efp = e_font_fontconfig_name_parse(efd->font);
523 if (efp->name)
524 {
525 Eina_Strbuf *buf;
526 Eina_List *l;
527 int size = efd->size;
528 char size_buf[12];
529 const char *p;
530
531 /* TODO better way to convert evas font sizes? */
532 if (!size) size = 12;
533 else if (size < 0) size /= -10;
534 else if (size < 5) size = 5;
535 else if (size > 25) size = 25;
536
537 /* Convert from pixels to point. */
538 snprintf(size_buf, sizeof(size_buf), "%1.1f", (float) size * 0.75);
539
540 buf = eina_strbuf_new();
541 eina_strbuf_append(buf, efp->name);
542 eina_strbuf_append_char(buf, ' ');
543 EINA_LIST_FOREACH(efp->styles, l, p)
544 {
545 eina_strbuf_append(buf, p);
546 eina_strbuf_append_char(buf, ' ');
547 }
548 eina_strbuf_append(buf, size_buf);
549 _e_xsettings_string_set(_setting_font_name,
550 eina_strbuf_string_get(buf));
551 eina_strbuf_free(buf);
552 e_font_properties_free(efp);
553 return;
554 }
555
556 e_font_properties_free(efp);
557 }
558
559 _e_xsettings_string_set(_setting_font_name, NULL);
560 }
561
562 #if 0
563 static void
564 _e_xsettings_xft_set(void)
565 {
566 if (e_config->scale.use_dpi)
567 _e_xsettings_int_set(_setting_xft_dpi,
568 e_config->scale.base_dpi, EINA_TRUE);
569 else
570 _e_xsettings_int_set(_setting_xft_dpi, 0, EINA_FALSE);
571 }
572
573 #endif
574
575 static void
_e_xsettings_cursor_path_set(void)576 _e_xsettings_cursor_path_set(void)
577 {
578 struct stat st;
579 char buf[PATH_MAX], env[PATH_MAX + PATH_MAX + 100], *path;
580
581 e_user_homedir_concat_static(buf, ".icons");
582
583 if (stat(buf, &st)) return;
584 path = getenv("XCURSOR_PATH");
585 if (path)
586 {
587 if (strstr(path, buf)) return;
588 snprintf(env, sizeof(env), "%s:%s", buf, path);
589 path = env;
590 }
591 else
592 {
593 snprintf(env, sizeof(env), "%s:%s", buf, "/usr/share/icons");
594 path = env;
595 }
596 e_env_set("XCURSOR_PATH", path);
597 }
598
599 static void
_e_xsettings_start(void)600 _e_xsettings_start(void)
601 {
602 if (running) return;
603
604 _e_xsettings_theme_set();
605 _e_xsettings_icon_theme_set();
606 _e_xsettings_font_set();
607 _e_xsettings_cursor_path_set();
608
609 manager = E_NEW(Settings_Manager, 1);
610
611 if (!_e_xsettings_activate(manager))
612 _e_xsettings_retry(manager);
613
614 running = EINA_TRUE;
615 }
616
617 static void
_e_xsettings_stop(void)618 _e_xsettings_stop(void)
619 {
620 Setting *s;
621
622 if (!running) return;
623
624 if (manager->timer_retry)
625 ecore_timer_del(manager->timer_retry);
626
627 if ((!stopping) && (!x_fatal))
628 _e_xsettings_deactivate(manager);
629
630 E_FREE(manager);
631
632 EINA_LIST_FREE(settings, s)
633 {
634 if (s->name) eina_stringshare_del(s->name);
635 if (s->s.value) eina_stringshare_del(s->s.value);
636 E_FREE(s);
637 }
638
639 running = EINA_FALSE;
640 }
641
642 EINTERN int
e_xsettings_init(void)643 e_xsettings_init(void)
644 {
645 _atom_manager = ecore_x_atom_get("MANAGER");
646 _atom_xsettings = ecore_x_atom_get("_XSETTINGS_SETTINGS");
647 _atom_gtk_iconthemes = ecore_x_atom_get("_GTK_LOAD_ICONTHEMES");
648 _atom_gtk_rcfiles = ecore_x_atom_get("_GTK_READ_RCFILES");
649
650 if (e_config->xsettings.enabled)
651 {
652 _e_xsettings_start();
653 if (!getenv("E_RESTART"))
654 _e_xsettings_gtk_rcfiles_update();
655 }
656
657 return 1;
658 }
659
660 EINTERN int
e_xsettings_shutdown(void)661 e_xsettings_shutdown(void)
662 {
663 _e_xsettings_stop();
664 if (eio_op) eio_file_cancel(eio_op);
665 eio_op = NULL;
666 setting = EINA_FALSE;
667
668 return 1;
669 }
670
671 E_API void
e_xsettings_config_update(void)672 e_xsettings_config_update(void)
673 {
674 if (!_atom_manager) return;
675 setting = EINA_FALSE;
676 if (eio_op) eio_file_cancel(eio_op);
677 if (!e_config->xsettings.enabled)
678 {
679 _e_xsettings_stop();
680 return;
681 }
682
683 if (!running)
684 {
685 _e_xsettings_start();
686 }
687 else
688 {
689 _e_xsettings_theme_set();
690 _e_xsettings_icon_theme_set();
691 _e_xsettings_font_set();
692 _e_xsettings_update();
693 _e_xsettings_gtk_icon_update();
694 reset = EINA_TRUE;
695 }
696 }
697
698