update for beta release
[framework/uifw/e17.git] / src / bin / e_xsettings.c
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 RETRY_TIMEOUT 2.0
10
11 #define SETTING_TYPE_INT        0
12 #define SETTING_TYPE_STRING     1
13 #define SETTING_TYPE_COLOR      2
14
15 #define OFFSET_ADD(n) ((n + 4 - 1) & (~(4 - 1)))
16
17 #define DBG printf
18
19 typedef struct _Settings_Manger Settings_Manager;
20 typedef struct _Setting Setting;
21
22 struct _Settings_Manger
23 {
24    E_Manager *man;
25    Ecore_X_Window selection;
26    Ecore_Timer *timer_retry;
27    unsigned long serial;
28    Ecore_X_Atom _atom_xsettings_screen;
29 };
30
31 struct _Setting
32 {
33   unsigned short type;
34
35   const char *name;
36
37   struct { const char *value; } s;
38   struct { int value; } i;
39   struct { unsigned short red, green, blue, alpha; } c;
40
41   unsigned long length;
42   unsigned long last_change;
43 };
44
45 static void _e_xsettings_apply(Settings_Manager *sm);
46
47 static Ecore_X_Atom _atom_manager = 0;
48 static Ecore_X_Atom _atom_xsettings = 0;
49 static Eina_List *managers = NULL;
50 static Eina_List *handlers = NULL;
51 static Eina_List *settings = NULL;
52 static Eina_Bool running = EINA_FALSE;
53 static const char _setting_icon_theme_name[] = "Net/IconThemeName";
54 static const char _setting_theme_name[]      = "Net/ThemeName";
55 static const char _setting_font_name[]       = "Gtk/FontName";
56 static const char _setting_xft_dpi[]         = "Xft/DPI";
57
58 static Ecore_X_Atom
59 _e_xsettings_atom_screen_get(int screen_num)
60 {
61    char buf[32];
62    snprintf(buf, sizeof(buf), "_XSETTINGS_S%d", screen_num);
63    return ecore_x_atom_get(buf);
64 }
65
66 static Eina_Bool
67 _e_xsettings_selection_owner_set(Settings_Manager *sm)
68 {
69    Ecore_X_Atom atom;
70    Ecore_X_Window cur_selection;
71    Eina_Bool ret;
72
73    atom = _e_xsettings_atom_screen_get(sm->man->num);
74    ecore_x_selection_owner_set(sm->selection, atom, ecore_x_current_time_get());
75    ecore_x_sync();
76    cur_selection = ecore_x_selection_owner_get(atom);
77
78    ret = (cur_selection == sm->selection);
79    if (!ret)
80      fprintf(stderr, "XSETTINGS: tried to set selection to %#x, but got %#x\n",
81              sm->selection, cur_selection);
82
83    return ret;
84 }
85
86 static void
87 _e_xsettings_deactivate(Settings_Manager *sm)
88 {
89    Ecore_X_Window old;
90
91    if (sm->selection == 0) return;
92
93    old = sm->selection;
94    sm->selection = 0;
95    _e_xsettings_selection_owner_set(sm);
96    ecore_x_sync();
97    ecore_x_window_free(old);
98 }
99
100 static Eina_Bool
101 _e_xsettings_activate(Settings_Manager *sm)
102 {
103    Ecore_X_Atom atom;
104    Ecore_X_Window old_win;
105
106    if (sm->selection != 0) return 1;
107
108    atom = _e_xsettings_atom_screen_get(sm->man->num);
109    old_win = ecore_x_selection_owner_get(atom);
110    if (old_win != 0) return 0;
111
112    sm->selection = ecore_x_window_input_new(0, 0, 0, 1, 1);
113    if (sm->selection == 0)
114      return 0;
115
116    if (!_e_xsettings_selection_owner_set(sm))
117      {
118         ecore_x_window_free(sm->selection);
119         sm->selection = 0;
120         return 0;
121      }
122
123    ecore_x_client_message32_send(e_manager_current_get()->root, _atom_manager,
124                                  ECORE_X_EVENT_MASK_WINDOW_CONFIGURE,
125                                  ecore_x_current_time_get(), atom,
126                                  sm->selection, 0, 0);
127
128    _e_xsettings_apply(sm);
129
130    return 1;
131 }
132
133 static Eina_Bool
134 _e_xsettings_activate_retry(void *data)
135 {
136    Settings_Manager *sm = data;
137    Eina_Bool ret;
138
139    fputs("XSETTINGS: reactivate...\n", stderr);
140    ret = _e_xsettings_activate(sm);
141    if (ret)
142      fputs("XSETTINGS: activate success!\n", stderr);
143    else
144      fprintf(stderr, "XSETTINGS: activate failure! retrying in %0.1f seconds\n",
145              RETRY_TIMEOUT);
146
147    if (!ret)
148      return ECORE_CALLBACK_RENEW;
149
150    sm->timer_retry = NULL;
151    return ECORE_CALLBACK_CANCEL;
152 }
153
154 static void
155 _e_xsettings_retry(Settings_Manager *sm)
156 {
157    if (sm->timer_retry) return;
158    sm->timer_retry = ecore_timer_add
159      (RETRY_TIMEOUT, _e_xsettings_activate_retry, sm);
160 }
161
162 static void
163 _e_xsettings_string_set(const char *name, const char *value)
164 {
165    Setting *s;
166    Eina_List *l;
167
168    if (!name) return;
169    name = eina_stringshare_add(name);
170
171    EINA_LIST_FOREACH(settings, l, s)
172      {
173         if (s->type != SETTING_TYPE_STRING) continue;
174         if (s->name == name) break;
175      }
176    if (!value)
177      {
178         if (!s) return;
179         DBG("remove %s\n", name);
180         eina_stringshare_del(name);
181         eina_stringshare_del(s->name);
182         eina_stringshare_del(s->s.value);
183         settings = eina_list_remove(settings, s);
184         E_FREE(s);
185         return;
186      }
187    if (s)
188      {
189         DBG("update %s %s\n", name, value);
190         eina_stringshare_del(name);
191         eina_stringshare_replace(&s->s.value, value);
192      }
193    else
194      {
195         DBG("add %s %s\n", name, value);
196         s = E_NEW(Setting, 1);
197         s->type = SETTING_TYPE_STRING;
198         s->name = name;
199         s->s.value = eina_stringshare_add(value);
200         settings = eina_list_append(settings, s);
201      }
202
203    /* type + pad + name-len + last-change-serial + str_len */
204    s->length = 12;
205    s->length += OFFSET_ADD(strlen(name));
206    s->length += OFFSET_ADD(strlen(value));
207    s->last_change = ecore_x_current_time_get();
208 }
209
210
211 static void
212 _e_xsettings_int_set(const char *name, int value, Eina_Bool set)
213 {
214    Setting *s;
215    Eina_List *l;
216
217    if (!name) return;
218    name = eina_stringshare_add(name);
219
220    EINA_LIST_FOREACH(settings, l, s)
221      {
222         if (s->type != SETTING_TYPE_INT) continue;
223         if (s->name == name) break;
224      }
225    if (!set)
226      {
227         if (!s) return;
228         DBG("remove %s\n", name);
229         eina_stringshare_del(name);
230         eina_stringshare_del(s->name);
231         settings = eina_list_remove(settings, s);
232         E_FREE(s);
233         return;
234      }
235    if (s)
236      {
237         DBG("update %s %d\n", name, value);
238         eina_stringshare_del(name);
239         s->i.value = value;
240      }
241    else
242      {
243         DBG("add %s %d\n", name, value);
244         s = E_NEW(Setting, 1);
245         s->type = SETTING_TYPE_INT;
246         s->name = name;
247         s->i.value = value;
248         settings = eina_list_append(settings, s);
249      }
250
251    // type + pad + name-len + last-change-serial + value
252    s->length = 12;
253    s->length += OFFSET_ADD(strlen(name));
254 }
255
256 static unsigned char *
257 _e_xsettings_copy(unsigned char *buffer, Setting *s)
258 {
259    size_t str_len;
260    size_t len;
261
262    buffer[0] = s->type;
263    buffer[1] = 0;
264    buffer += 2;
265
266    str_len = strlen(s->name);
267    *(CARD16 *)(buffer) = str_len;
268    buffer += 2;
269
270    memcpy(buffer, s->name, str_len);
271    buffer += str_len;
272
273    len = OFFSET_ADD(str_len) - str_len;
274    memset(buffer, 0, len);
275    buffer += len;
276
277    *(CARD32 *)(buffer) = s->last_change;
278    buffer += 4;
279
280    switch (s->type)
281      {
282       case SETTING_TYPE_INT:
283          *(CARD32 *)(buffer) = s->i.value;
284          buffer += 4;
285          break;
286
287       case SETTING_TYPE_STRING:
288          str_len = strlen (s->s.value);
289          *(CARD32 *)(buffer) = str_len;
290          buffer += 4;
291
292          memcpy(buffer, s->s.value, str_len);
293          buffer += str_len;
294
295          len = OFFSET_ADD(str_len) - str_len;
296          memset(buffer, 0, len);
297          buffer += len;
298          break;
299
300       case SETTING_TYPE_COLOR:
301          *(CARD16 *)(buffer) = s->c.red;
302          *(CARD16 *)(buffer + 2) = s->c.green;
303          *(CARD16 *)(buffer + 4) = s->c.blue;
304          *(CARD16 *)(buffer + 6) = s->c.alpha;
305          buffer += 8;
306          break;
307      }
308
309    return buffer;
310 }
311
312 static void
313 _e_xsettings_apply(Settings_Manager *sm)
314 {
315    unsigned char *data;
316    unsigned char *pos;
317    size_t len = 12;
318    Setting *s;
319    Eina_List *l;
320
321    EINA_LIST_FOREACH(settings, l, s)
322      len += s->length;
323
324    pos = data = malloc(len);
325    if (!data) return;
326
327 #if __BYTE_ORDER == __LITTLE_ENDIAN
328    *pos = LSBFirst;
329 #else
330    *pos = MSBFirst;
331 #endif
332
333    pos += 4;
334    *(CARD32*)pos = sm->serial++;
335    pos += 4;
336    *(CARD32*)pos = eina_list_count(settings);
337    pos += 4;
338
339    EINA_LIST_FOREACH(settings, l, s)
340      pos = _e_xsettings_copy(pos, s);
341
342    ecore_x_window_prop_property_set(sm->selection,
343                                     _atom_xsettings,
344                                     _atom_xsettings,
345                                     8, data, len);
346    free(data);
347 }
348
349 static void
350 _e_xsettings_update(void)
351 {
352    Settings_Manager *sm;
353    Eina_List *l;
354
355    EINA_LIST_FOREACH(managers, l, sm)
356      if (sm->selection) _e_xsettings_apply(sm);
357 }
358
359 static Eina_Bool
360 _cb_icon_theme_change(void *data __UNUSED__, int type __UNUSED__, void *event)
361 {
362    E_Event_Config_Icon_Theme *ev = event;
363
364    if (e_config->xsettings.match_e17_icon_theme)
365      {
366         _e_xsettings_string_set(_setting_icon_theme_name,
367                               ev->icon_theme);
368         _e_xsettings_update();
369      }
370
371    return ECORE_CALLBACK_PASS_ON;
372 }
373
374
375 static void
376 _e_xsettings_icon_theme_set(void)
377 {
378    if (e_config->xsettings.match_e17_icon_theme)
379      {
380         _e_xsettings_string_set(_setting_icon_theme_name,
381                                 e_config->icon_theme);
382         return;
383      }
384
385    if (e_config->xsettings.net_icon_theme_name)
386      {
387         _e_xsettings_string_set(_setting_icon_theme_name,
388                               e_config->xsettings.net_icon_theme_name);
389         return;
390      }
391
392    _e_xsettings_string_set(_setting_icon_theme_name, NULL);
393 }
394
395 static void
396 _e_xsettings_theme_set(void)
397 {
398    if (e_config->xsettings.match_e17_theme)
399      {
400         E_Config_Theme *ct;
401         if ((ct = e_theme_config_get("theme")))
402           {
403              char *theme;
404
405              if ((theme = edje_file_data_get(ct->file, "gtk-theme")))
406                {
407                   char buf[4096], *dir;
408                   Eina_List *xdg_dirs, *l;
409
410                   e_user_homedir_snprintf(buf, sizeof(buf), ".themes/%s", theme);
411                   if (ecore_file_exists(buf))
412                     {
413                        _e_xsettings_string_set(_setting_theme_name, theme);
414                        return;
415                     }
416
417                   xdg_dirs = efreet_data_dirs_get();
418                   EINA_LIST_FOREACH(xdg_dirs, l, dir)
419                     {
420                        snprintf(buf, sizeof(buf), "%s/themes/%s", dir, theme);
421                        if (ecore_file_exists(buf))
422                          {
423                             _e_xsettings_string_set(_setting_theme_name, theme);
424                             return;
425                          }
426                     }
427                }
428           }
429      }
430
431    if (e_config->xsettings.net_theme_name)
432      {
433         _e_xsettings_string_set(_setting_theme_name,
434                               e_config->xsettings.net_theme_name);
435         return;
436      }
437
438    _e_xsettings_string_set(_setting_theme_name, NULL);
439 }
440
441 static void
442 _e_xsettings_font_set(void)
443 {
444    E_Font_Default *efd;
445    E_Font_Properties *efp;
446
447    efd = e_font_default_get("application");
448
449    if (efd && efd->font)
450      {
451         efp = e_font_fontconfig_name_parse(efd->font);
452         if (efp->name)
453           {
454              int size = efd->size;
455              char buf[128];
456              /* TODO better way to convert evas font sizes? */
457              if (size < 0) size /= -10;
458              if (size < 5) size = 5;
459              if (size > 25) size = 25;
460
461              snprintf(buf, sizeof(buf), "%s %d", efp->name, size);
462              _e_xsettings_string_set(_setting_font_name, buf);
463              e_font_properties_free(efp);
464              return;
465           }
466
467         e_font_properties_free(efp);
468      }
469
470    _e_xsettings_string_set(_setting_font_name, NULL);
471 }
472
473 static void
474 _e_xsettings_xft_set(void)
475 {
476
477    if (e_config->scale.use_dpi)
478      _e_xsettings_int_set(_setting_xft_dpi, e_config->scale.base_dpi, EINA_TRUE);
479    else
480      _e_xsettings_int_set(_setting_xft_dpi, 0, EINA_FALSE);
481
482 }
483
484 static void
485 _e_xsettings_start(void)
486 {
487    Eina_List *l;
488    E_Manager *man;
489
490    if (running) return;
491
492    _e_xsettings_theme_set();
493    _e_xsettings_icon_theme_set();
494    _e_xsettings_font_set();
495
496    EINA_LIST_FOREACH(e_manager_list(), l, man)
497      {
498         Settings_Manager *sm = E_NEW(Settings_Manager, 1);
499         sm->man = man;
500
501         if (!_e_xsettings_activate(sm))
502           _e_xsettings_retry(sm);
503
504         managers = eina_list_append(managers, sm);
505      }
506
507    handlers = eina_list_append(handlers, ecore_event_handler_add(E_EVENT_CONFIG_ICON_THEME,
508                                                                  _cb_icon_theme_change, NULL));
509
510    running = EINA_TRUE;
511 }
512
513 static void
514 _e_xsettings_stop(void)
515 {
516    Settings_Manager *sm;
517    Ecore_Event_Handler *h;
518    Setting *s;
519
520    if (!running) return;
521
522    EINA_LIST_FREE(managers, sm)
523      {
524         if (sm->timer_retry)
525           ecore_timer_del(sm->timer_retry);
526
527         _e_xsettings_deactivate(sm);
528
529         E_FREE(sm);
530      }
531
532    EINA_LIST_FREE(settings, s)
533      {
534         if (s->name) eina_stringshare_del(s->name);
535         if (s->s.value) eina_stringshare_del(s->s.value);
536         E_FREE(s);
537      }
538
539    EINA_LIST_FREE(handlers, h)
540      ecore_event_handler_del(h);
541
542    running = EINA_FALSE;
543 }
544
545 EINTERN int
546 e_xsettings_init(void)
547 {
548    _atom_manager = ecore_x_atom_get("MANAGER");
549    _atom_xsettings = ecore_x_atom_get("_XSETTINGS_SETTINGS");
550
551    if (e_config->xsettings.enabled)
552      _e_xsettings_start();
553
554    return 1;
555 }
556
557 EINTERN int
558 e_xsettings_shutdown(void)
559 {
560    _e_xsettings_stop();
561
562    return 1;
563 }
564
565 EAPI void
566 e_xsettings_config_update(void)
567 {
568    if (!e_config->xsettings.enabled)
569      {
570         _e_xsettings_stop();
571         return;
572      }
573
574    if (!running)
575      {
576         _e_xsettings_start();
577      }
578    else
579      {
580         _e_xsettings_theme_set();
581         _e_xsettings_icon_theme_set();
582         _e_xsettings_font_set();
583         _e_xsettings_update();
584      }
585 }