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 "xevent.h"
37 #include "xsettings-manager.h"
38 #include "xutils.h"
39 
40 #include <X11/XKBlib.h>
41 
42 static XSettingsManager **managers = NULL;
43 
44 /* FORWARDS */
45 gboolean settings_daemon_start(GKeyFile* kf);
46 void settings_manager_selection_clear( XEvent* evt );
47 void settings_daemon_reload(GKeyFile* kf);
48 /* End FORWARDS */
49 
terminate_cb(void * data)50 static void terminate_cb (void *data)
51 {
52 	gboolean *terminated = data;
53 
54 	if (*terminated)
55 		return;
56 
57 	*terminated = TRUE;
58 	exit( 0 );
59 //	gtk_main_quit ();
60 }
61 
merge_xrdb(const char * content,int len)62 static void merge_xrdb(const char* content, int len)
63 {
64     gchar* argv[] = { "xrdb", "-merge", "-", NULL };
65     GPid pid;
66     int stdi, status, w;
67     if( g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH,
68                         NULL, NULL, &pid, &stdi, NULL, NULL, NULL ) )
69     {
70         if (len < 0)
71         {
72             w = write( stdi, content, strlen(content));
73         }
74         else
75         {
76             w = write( stdi, content, len);
77         }
78         close(stdi);
79         waitpid( pid, &status, 0 );
80     }
81 }
82 
83 
84 /* This function is taken from Gnome's control-center 2.6.0.3 (gnome-settings-mouse.c) and was modified*/
85 #define DEFAULT_PTR_MAP_SIZE 128
set_left_handed_mouse(gboolean mouse_left_handed)86 static void set_left_handed_mouse( gboolean mouse_left_handed )
87 {
88     unsigned char *buttons;
89     gint n_buttons, i;
90     gint idx_1 = 0, idx_3 = 1;
91 
92     buttons = g_alloca (DEFAULT_PTR_MAP_SIZE);
93     n_buttons = XGetPointerMapping (dpy, buttons, DEFAULT_PTR_MAP_SIZE);
94     if (n_buttons > DEFAULT_PTR_MAP_SIZE)
95     {
96         buttons = g_alloca (n_buttons);
97         n_buttons = XGetPointerMapping (dpy, buttons, n_buttons);
98     }
99 
100     for (i = 0; i < n_buttons; i++)
101     {
102         if (buttons[i] == 1)
103             idx_1 = i;
104         else if (buttons[i] == ((n_buttons < 3) ? 2 : 3))
105             idx_3 = i;
106     }
107 
108     if ((mouse_left_handed && idx_1 < idx_3) ||
109         (!mouse_left_handed && idx_1 > idx_3))
110     {
111         buttons[idx_1] = ((n_buttons < 3) ? 2 : 3);
112         buttons[idx_3] = 1;
113         XSetPointerMapping (dpy, buttons, n_buttons);
114     }
115 }
116 
configure_input(GKeyFile * kf)117 static void configure_input(GKeyFile* kf)
118 {
119     XKeyboardControl values;
120 
121     /* Mouse settings */
122     int accel_factor, accel_threshold, delay, interval;
123     gboolean left_handed, beep;
124 
125     accel_factor = g_key_file_get_integer(kf, "Mouse", "AccFactor", NULL);
126     accel_threshold = g_key_file_get_integer(kf, "Mouse", "AccThreshold", NULL);
127     if( accel_factor || accel_threshold )
128     {
129         XChangePointerControl(dpy, accel_factor != 0, accel_threshold != 0,
130                                  accel_factor, 10, accel_threshold);
131     }
132 
133     left_handed = g_key_file_get_integer(kf, "Mouse", "LeftHanded", NULL);
134     set_left_handed_mouse(left_handed);
135 
136     /* Keyboard settings */
137     if(XkbGetAutoRepeatRate(dpy, XkbUseCoreKbd, (unsigned int*) &delay, (unsigned int*) &interval))
138     {
139         int val;
140         val = g_key_file_get_integer(kf, "Keyboard", "Delay", NULL);
141         if(val > 0)
142             delay = val;
143         val = g_key_file_get_integer(kf, "Keyboard", "Interval", NULL);
144         if(val > 0)
145             interval = val;
146         if( val > 0 )
147         {
148             XkbSetAutoRepeatRate(dpy, XkbUseCoreKbd, delay, interval);
149         }
150     }
151 
152     beep = g_key_file_get_integer(kf, "Keyboard", "Beep", NULL);
153     values.bell_percent = beep ? -1 : 0;
154     XChangeKeyboardControl(dpy, KBBellPercent, &values);
155 }
156 
load_settings(GKeyFile * kf)157 static void load_settings( GKeyFile* kf )
158 {
159     GString* buf;
160     char* str;
161     int val;
162 
163 	int i;
164 	const char group[] = "GTK";
165 	char** keys, **key;
166 
167 	/* Mouse cursor (does this work?) */
168 	str = g_key_file_get_string( kf, group, "sGtk/CursorThemeName", NULL);
169 	val = g_key_file_get_integer(kf, group, "iGtk/CursorThemeSize", NULL);
170 	if(str || val > 0)
171 	{
172 		buf = g_string_sized_new(100);
173 		if(str)
174 		{
175 			if(*str)
176 				g_string_append_printf(buf, "Xcursor.theme:%s\n", str);
177 			g_free(str);
178 		}
179 		g_string_append(buf, "Xcursor.theme_core:true\n");
180 		if(val > 0)
181 			g_string_append_printf(buf, "Xcursor.size:%d\n", val);
182 		merge_xrdb( buf->str, buf->len );
183 		g_string_free(buf, TRUE);
184 	}
185 
186 	/* Load mouse and keyboard settings */
187 	configure_input(kf);
188 
189 	/* Load GTK+ settings */
190 	if ( (keys = g_key_file_get_keys( kf, group, NULL, NULL )) == NULL )
191 	    return;
192 
193 	for( key = keys; *key; ++key )
194 	{
195 		const char* name = *key + 1;
196 
197 		switch( **key )
198 		{
199 			case 's':	/* string */
200 			{
201 				str = g_key_file_get_string( kf, group, *key, NULL );
202 				if( str )
203 				{
204 					for( i = 0; managers[i]; ++i )
205 						xsettings_manager_set_string( managers [i], name, str );
206 					g_free( str );
207 				}
208 				else
209 				{
210 					for( i = 0; managers[i]; ++i )
211 						xsettings_manager_delete_setting( managers[i], name );
212 				}
213 				break;
214 			}
215 			case 'i':	/* integer */
216 			{
217 				val = g_key_file_get_integer( kf, group, *key, NULL );
218 				for( i = 0; managers[i]; ++i )
219 					xsettings_manager_set_int( managers [i], name, val );
220 				break;
221 			}
222 			case 'c':	/* color */
223 			{
224 				gsize len = 0;
225 				int* vals = g_key_file_get_integer_list( kf, group, *key, &len, NULL );
226 				if( vals && len >= 3 )
227 				{
228 					XSettingsColor color;
229 					color.red = (gushort)vals[0];
230 					color.green = (gushort)vals[1];
231 					color.blue = (gushort)vals[2];
232 					color.alpha = (gushort)( len >3 ? vals[3] : 65535 );
233 					for( i = 0; managers[i]; ++i )
234 						xsettings_manager_set_color( managers [i], name, &color );
235 				}
236 				else
237 				{
238 					for( i = 0; managers[i]; ++i )
239 						xsettings_manager_delete_setting( managers[i], name );
240 				}
241 				g_free( vals );
242 				break;
243 			}
244 		}
245 	}
246 
247 	for( i = 0; managers[i]; ++i )
248 		xsettings_manager_notify( managers [i] );
249 }
250 
create_xsettings_managers()251 static gboolean create_xsettings_managers()
252 {
253 	int n_screens = ScreenCount(dpy);
254 	int i;
255 	gboolean terminated = FALSE;
256 
257 	if (xsettings_manager_check_running( dpy, n_screens) )
258 	{
259 		g_error ("You can only run one xsettings manager at a time; exiting\n");
260 		return FALSE;
261 	}
262 
263 	managers = g_new (XSettingsManager *, n_screens + 1);
264 	for( i = 0; i < n_screens; ++i )
265 	{
266 		Screen *screen;
267 		screen = ScreenOfDisplay( dpy, i );
268 		managers [i] = xsettings_manager_new ( dpy, i, terminate_cb, &terminated);
269 		if(!managers [i])
270 		{
271 			g_error("Could not create xsettings manager for screen %d!\n", i);
272 			return FALSE;
273 		}
274 		XSelectInput( dpy, RootWindow(dpy, i), SubstructureNotifyMask | PropertyChangeMask );
275 	}
276 	managers [i] = NULL;
277 
278 	return TRUE;
279 }
280 
settings_daemon_start(GKeyFile * kf)281 gboolean settings_daemon_start(GKeyFile* kf)
282 {
283        /* initialize X-related stuff and connect to X Display */
284        if( G_UNLIKELY(! xevent_init() ) )
285           return FALSE;
286 
287 	if( ! create_xsettings_managers() )
288 		return FALSE;
289 
290 	load_settings(kf);
291 
292 	/* sync with X11 to prevent some racing conditions:
293 	 * For example: if gtk+ applications are started before
294 	 * XSETTINGS properties are properly set on root window,
295 	 * they cannot correctly use settings from Xsettings daemon. */
296 	XSync(dpy, FALSE);
297 
298 	return TRUE;
299 }
300 
settings_manager_selection_clear(XEvent * evt)301 void settings_manager_selection_clear( XEvent* evt )
302 {
303 	XSettingsManager**mgr;
304 	for( mgr = managers; *mgr; ++mgr )
305 	{
306 		if( xsettings_manager_get_window( *mgr ) == evt->xany.window )
307 			xsettings_manager_process_event( *mgr, evt );
308 	}
309 }
310 
settings_daemon_reload(GKeyFile * kf)311 void settings_daemon_reload(GKeyFile* kf)
312 {
313 	if(kf)
314 	{
315 		load_settings(kf);
316 	}
317 }
318