Move new lxsession with built-in settings daemon to trunk.
[lxde/lxsession.git] / lxsession / settings-daemon.c
1 /*
2 * lxde-settings.c - XSettings daemon of LXDE
3 *
4 * Copyright 2008 PCMan <pcman.tw@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <glib.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <locale.h>
35
36 #include "lxsession.h"
37 #include "xevent.h"
38 #include "xsettings-manager.h"
39 #include "xutils.h"
40
41 #include <X11/XKBlib.h>
42
43 static XSettingsManager **managers = NULL;
44
45 static void terminate_cb (void *data)
46 {
47 gboolean *terminated = data;
48
49 if (*terminated)
50 return;
51
52 *terminated = TRUE;
53 exit( 0 );
54 // gtk_main_quit ();
55 }
56
57 static void merge_xrdb(const char* content, int len)
58 {
59 char* argv[] = { "xrdb", "-merge", "-", NULL };
60 GPid pid;
61 int stdi, status;
62 if( g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
63 NULL, NULL, &pid, &stdi, NULL, NULL, NULL ) )
64 {
65 write( stdi, content, len < 0 ? strlen(content) : len );
66 close(stdi);
67 waitpid( pid, &status, 0 );
68 }
69 }
70
71
72 /* This function is taken from Gnome's control-center 2.6.0.3 (gnome-settings-mouse.c) and was modified*/
73 #define DEFAULT_PTR_MAP_SIZE 128
74 static void set_left_handed_mouse( gboolean mouse_left_handed )
75 {
76 unsigned char *buttons;
77 gint n_buttons, i;
78 gint idx_1 = 0, idx_3 = 1;
79
80 buttons = g_alloca (DEFAULT_PTR_MAP_SIZE);
81 n_buttons = XGetPointerMapping (dpy, buttons, DEFAULT_PTR_MAP_SIZE);
82 if (n_buttons > DEFAULT_PTR_MAP_SIZE)
83 {
84 buttons = g_alloca (n_buttons);
85 n_buttons = XGetPointerMapping (dpy, buttons, n_buttons);
86 }
87
88 for (i = 0; i < n_buttons; i++)
89 {
90 if (buttons[i] == 1)
91 idx_1 = i;
92 else if (buttons[i] == ((n_buttons < 3) ? 2 : 3))
93 idx_3 = i;
94 }
95
96 if ((mouse_left_handed && idx_1 < idx_3) ||
97 (!mouse_left_handed && idx_1 > idx_3))
98 {
99 buttons[idx_1] = ((n_buttons < 3) ? 2 : 3);
100 buttons[idx_3] = 1;
101 XSetPointerMapping (dpy, buttons, n_buttons);
102 }
103 }
104
105 static void configure_input(GKeyFile* kf)
106 {
107 XKeyboardControl values;
108
109 /* Mouse settings */
110 int accel_factor, accel_threshold, delay, interval;
111 gboolean left_handed, beep;
112
113 accel_factor = g_key_file_get_integer(kf, "Mouse", "AccFactor", NULL);
114 accel_threshold = g_key_file_get_integer(kf, "Mouse", "AccThreshold", NULL);
115 if( accel_factor || accel_threshold )
116 {
117 XChangePointerControl(dpy, accel_factor != 0, accel_threshold != 0,
118 accel_factor, 10, accel_threshold);
119 }
120
121 left_handed = g_key_file_get_integer(kf, "Mouse", "LeftHanded", NULL);
122 set_left_handed_mouse(left_handed);
123
124 /* Keyboard settings */
125 if(XkbGetAutoRepeatRate(dpy, XkbUseCoreKbd, &delay, &interval))
126 {
127 int val;
128 val = g_key_file_get_integer(kf, "Keyboard", "Delay", NULL);
129 if(val > 0)
130 delay = val;
131 val = g_key_file_get_integer(kf, "Keyboard", "Interval", NULL);
132 if(val > 0)
133 interval = val;
134 if( val > 0 )
135 {
136 XkbSetAutoRepeatRate(dpy, XkbUseCoreKbd, delay, interval);
137 }
138 }
139
140 beep = g_key_file_get_integer(kf, "Keyboard", "Beep", NULL);
141 values.bell_percent = beep ? -1 : 0;
142 XChangeKeyboardControl(dpy, KBBellPercent, &values);
143 }
144
145 static void load_settings( GKeyFile* kf )
146 {
147 gboolean ret;
148 char* file;
149 GString* buf;
150 char* str;
151 int val;
152
153 int i;
154 const char group[] = "GTK";
155 char** keys, **key;
156
157 /* Mouse cursor (does this work?) */
158 str = g_key_file_get_string( kf, group, "sGtk/CursorThemeName", NULL);
159 val = g_key_file_get_integer(kf, group, "iGtk/CursorThemeSize", NULL);
160 if(str || val > 0)
161 {
162 buf = g_string_sized_new(100);
163 if(str)
164 {
165 if(*str)
166 g_string_append_printf(buf, "Xcursor.theme:%s\n", str);
167 g_free(str);
168 }
169 g_string_append(buf, "Xcursor.theme_core:true\n");
170 if(val > 0)
171 g_string_append_printf(buf, "Xcursor.size:%d\n", val);
172 merge_xrdb( buf->str, buf->len );
173 g_string_free(buf, TRUE);
174 }
175
176 /* Load mouse and keyboard settings */
177 configure_input(kf);
178
179 /* Load GTK+ settings */
180 keys = g_key_file_get_keys( kf, group, NULL, NULL );
181 for( key = keys; *key; ++key )
182 {
183 const char* name = *key + 1;
184
185 switch( **key )
186 {
187 case 's': /* string */
188 {
189 str = g_key_file_get_string( kf, group, *key, NULL );
190 if( str )
191 {
192 for( i = 0; managers[i]; ++i )
193 xsettings_manager_set_string( managers [i], name, str );
194 g_free( str );
195 }
196 else
197 {
198 for( i = 0; managers[i]; ++i )
199 xsettings_manager_delete_setting( managers[i], name );
200 }
201 break;
202 }
203 case 'i': /* integer */
204 {
205 val = g_key_file_get_integer( kf, group, *key, NULL );
206 for( i = 0; managers[i]; ++i )
207 xsettings_manager_set_int( managers [i], name, val );
208 break;
209 }
210 case 'c': /* color */
211 {
212 gsize len = 0;
213 int* vals = g_key_file_get_integer_list( kf, group, *key, &len, NULL );
214 if( vals && len >= 3 )
215 {
216 XSettingsColor color;
217 color.red = (gushort)vals[0];
218 color.green = (gushort)vals[1];
219 color.blue = (gushort)vals[2];
220 color.alpha = (gushort)( len >3 ? vals[3] : 65535 );
221 for( i = 0; managers[i]; ++i )
222 xsettings_manager_set_color( managers [i], name, &color );
223 }
224 else
225 {
226 for( i = 0; managers[i]; ++i )
227 xsettings_manager_delete_setting( managers[i], name );
228 }
229 g_free( vals );
230 break;
231 }
232 }
233 }
234
235 for( i = 0; managers[i]; ++i )
236 xsettings_manager_notify( managers [i] );
237 }
238
239 static gboolean create_xsettings_managers()
240 {
241 int n_screens = ScreenCount(dpy);
242 int i;
243 gboolean terminated = FALSE;
244
245 if (xsettings_manager_check_running( dpy, n_screens) )
246 {
247 g_error ("You can only run one xsettings manager at a time; exiting\n");
248 return FALSE;
249 }
250
251 managers = g_new (XSettingsManager *, n_screens + 1);
252 for( i = 0; i < n_screens; ++i )
253 {
254 Screen *screen;
255 screen = ScreenOfDisplay( dpy, i );
256 managers [i] = xsettings_manager_new ( dpy, i, terminate_cb, &terminated);
257 if(!managers [i])
258 {
259 g_error("Could not create xsettings manager for screen %d!\n", i);
260 return FALSE;
261 }
262 XSelectInput( dpy, RootWindow(dpy, i), SubstructureNotifyMask | PropertyChangeMask );
263 }
264 managers [i] = NULL;
265
266 return TRUE;
267 }
268
269 gboolean start_settings_daemon(GKeyFile* kf)
270 {
271 if( ! create_xsettings_managers() )
272 return FALSE;
273
274 load_settings(kf);
275
276 /* sync with X11 to prevent some racing conditions:
277 * For example: if gtk+ applications are started before
278 * XSETTINGS properties are properly set on root window,
279 * they cannot correctly use settings from Xsettings daemon. */
280 XSync(dpy, FALSE);
281
282 return TRUE;
283 }
284
285 void settings_manager_selection_clear( XEvent* evt )
286 {
287 XSettingsManager**mgr;
288 for( mgr = managers; *mgr; ++mgr )
289 {
290 if( xsettings_manager_get_window( *mgr ) == evt->xany.window )
291 xsettings_manager_process_event( *mgr, evt );
292 }
293 }
294
295 void settings_deamon_reload()
296 {
297 GKeyFile* kf = load_session_config(CONFIG_FILE_NAME);
298 if(kf)
299 {
300 load_settings(kf);
301 }
302 }