1 /*
2  * Copyright © 2001 Red Hat, Inc.
3  * Copyright (C) 2012-2021 MATE Developers
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation, and that the name of Red Hat not be used in advertising or
10  * publicity pertaining to distribution of the software without specific,
11  * written prior permission.  Red Hat makes no representations about the
12  * suitability of this software for any purpose.  It is provided "as is"
13  * without express or implied warranty.
14  *
15  * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
17  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
19  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21  *
22  * Author:  Owen Taylor, Red Hat, Inc.
23  */
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include <X11/Xmd.h>		/* For CARD16 */
29 
30 #include "xsettings-manager.h"
31 
32 struct _XSettingsManager
33 {
34   Display *display;
35   int screen;
36 
37   Window window;
38   Atom manager_atom;
39   Atom selection_atom;
40   Atom xsettings_atom;
41 
42   XSettingsTerminateFunc terminate;
43   void *cb_data;
44 
45   XSettingsList *settings;
46   unsigned long serial;
47 };
48 
49 static XSettingsList *settings;
50 
51 typedef struct
52 {
53   Window window;
54   Atom timestamp_prop_atom;
55 } TimeStampInfo;
56 
57 static Bool
timestamp_predicate(Display * display,XEvent * xevent,XPointer arg)58 timestamp_predicate (Display *display,
59 		     XEvent  *xevent,
60 		     XPointer arg)
61 {
62   TimeStampInfo *info = (TimeStampInfo *)arg;
63 
64   if (xevent->type == PropertyNotify &&
65       xevent->xproperty.window == info->window &&
66       xevent->xproperty.atom == info->timestamp_prop_atom)
67     return True;
68 
69   return False;
70 }
71 
72 /**
73  * get_server_time:
74  * @display: display from which to get the time
75  * @window: a #Window, used for communication with the server.
76  *          The window must have PropertyChangeMask in its
77  *          events mask or a hang will result.
78  *
79  * Routine to get the current X server time stamp.
80  *
81  * Return value: the time stamp.
82  **/
83 static Time
get_server_time(Display * display,Window window)84 get_server_time (Display *display,
85 		 Window   window)
86 {
87   unsigned char c = 'a';
88   XEvent xevent;
89   TimeStampInfo info;
90 
91   info.timestamp_prop_atom = XInternAtom  (display, "_TIMESTAMP_PROP", False);
92   info.window = window;
93 
94   XChangeProperty (display, window,
95 		   info.timestamp_prop_atom, info.timestamp_prop_atom,
96 		   8, PropModeReplace, &c, 1);
97 
98   XIfEvent (display, &xevent,
99 	    timestamp_predicate, (XPointer)&info);
100 
101   return xevent.xproperty.time;
102 }
103 
104 Bool
xsettings_manager_check_running(Display * display,int screen)105 xsettings_manager_check_running (Display *display,
106 				 int      screen)
107 {
108   char buffer[256];
109   Atom selection_atom;
110 
111   sprintf(buffer, "_XSETTINGS_S%d", screen);
112   selection_atom = XInternAtom (display, buffer, False);
113 
114   if (XGetSelectionOwner (display, selection_atom))
115     return True;
116   else
117     return False;
118 }
119 
120 XSettingsManager *
xsettings_manager_new(Display * display,int screen,XSettingsTerminateFunc terminate,void * cb_data)121 xsettings_manager_new (Display                *display,
122 		       int                     screen,
123 		       XSettingsTerminateFunc  terminate,
124 		       void                   *cb_data)
125 {
126   XSettingsManager *manager;
127   Time timestamp;
128   XClientMessageEvent xev;
129 
130   char buffer[256];
131 
132   manager = malloc (sizeof *manager);
133   if (!manager)
134     return NULL;
135 
136   manager->display = display;
137   manager->screen = screen;
138 
139   sprintf(buffer, "_XSETTINGS_S%d", screen);
140   manager->selection_atom = XInternAtom (display, buffer, False);
141   manager->xsettings_atom = XInternAtom (display, "_XSETTINGS_SETTINGS", False);
142   manager->manager_atom = XInternAtom (display, "MANAGER", False);
143 
144   manager->terminate = terminate;
145   manager->cb_data = cb_data;
146 
147   manager->settings = NULL;
148   manager->serial = 0;
149 
150   manager->window = XCreateSimpleWindow (display,
151 					 RootWindow (display, screen),
152 					 0, 0, 10, 10, 0,
153 					 WhitePixel (display, screen),
154 					 WhitePixel (display, screen));
155 
156   XSelectInput (display, manager->window, PropertyChangeMask);
157   timestamp = get_server_time (display, manager->window);
158 
159   XSetSelectionOwner (display, manager->selection_atom,
160 		      manager->window, timestamp);
161 
162   /* Check to see if we managed to claim the selection. If not,
163    * we treat it as if we got it then immediately lost it
164    */
165 
166   if (XGetSelectionOwner (display, manager->selection_atom) ==
167       manager->window)
168     {
169       xev.type = ClientMessage;
170       xev.window = RootWindow (display, screen);
171       xev.message_type = manager->manager_atom;
172       xev.format = 32;
173       xev.data.l[0] = timestamp;
174       xev.data.l[1] = manager->selection_atom;
175       xev.data.l[2] = manager->window;
176       xev.data.l[3] = 0;	/* manager specific data */
177       xev.data.l[4] = 0;	/* manager specific data */
178 
179       XSendEvent (display, RootWindow (display, screen),
180 		  False, StructureNotifyMask, (XEvent *)&xev);
181     }
182   else
183     {
184       manager->terminate (manager->cb_data);
185     }
186 
187   return manager;
188 }
189 
190 void
xsettings_manager_destroy(XSettingsManager * manager)191 xsettings_manager_destroy (XSettingsManager *manager)
192 {
193   XDestroyWindow (manager->display, manager->window);
194 
195   xsettings_list_free (manager->settings);
196   free (manager);
197 }
198 
199 Window
xsettings_manager_get_window(XSettingsManager * manager)200 xsettings_manager_get_window (XSettingsManager *manager)
201 {
202   return manager->window;
203 }
204 
205 Bool
xsettings_manager_process_event(XSettingsManager * manager,XEvent * xev)206 xsettings_manager_process_event (XSettingsManager *manager,
207 				 XEvent           *xev)
208 {
209   if (xev->xany.window == manager->window &&
210       xev->xany.type == SelectionClear &&
211       xev->xselectionclear.selection == manager->selection_atom)
212     {
213       manager->terminate (manager->cb_data);
214       return True;
215     }
216 
217   return False;
218 }
219 
220 XSettingsResult
xsettings_manager_delete_setting(XSettingsManager * manager,const char * name)221 xsettings_manager_delete_setting (XSettingsManager *manager,
222                                   const char       *name)
223 {
224   return xsettings_list_delete (&settings, name);
225 }
226 
227 XSettingsResult
xsettings_manager_set_setting(XSettingsManager * manager,XSettingsSetting * setting)228 xsettings_manager_set_setting (XSettingsManager *manager,
229 			       XSettingsSetting *setting)
230 {
231   XSettingsSetting *old_setting = xsettings_list_lookup (settings, setting->name);
232   XSettingsSetting *new_setting;
233   XSettingsResult result;
234 
235   if (old_setting)
236     {
237       if (xsettings_setting_equal (old_setting, setting))
238 	return XSETTINGS_SUCCESS;
239 
240       xsettings_list_delete (&settings, setting->name);
241     }
242 
243   new_setting = xsettings_setting_copy (setting);
244   if (!new_setting)
245     return XSETTINGS_NO_MEM;
246 
247   new_setting->last_change_serial = manager->serial;
248 
249   result = xsettings_list_insert (&settings, new_setting);
250 
251   if (result != XSETTINGS_SUCCESS)
252     xsettings_setting_free (new_setting);
253 
254   return result;
255 }
256 
257 XSettingsResult
xsettings_manager_set_int(XSettingsManager * manager,const char * name,int value)258 xsettings_manager_set_int (XSettingsManager *manager,
259 			   const char       *name,
260 			   int               value)
261 {
262   XSettingsSetting setting;
263 
264   setting.name = (char *)name;
265   setting.type = XSETTINGS_TYPE_INT;
266   setting.data.v_int = value;
267 
268   return xsettings_manager_set_setting (manager, &setting);
269 }
270 
271 XSettingsResult
xsettings_manager_set_string(XSettingsManager * manager,const char * name,const char * value)272 xsettings_manager_set_string (XSettingsManager *manager,
273 			      const char       *name,
274 			      const char       *value)
275 {
276   XSettingsSetting setting;
277 
278   setting.name = (char *)name;
279   setting.type = XSETTINGS_TYPE_STRING;
280   setting.data.v_string = (char *)value;
281 
282   return xsettings_manager_set_setting (manager, &setting);
283 }
284 
285 XSettingsResult
xsettings_manager_set_color(XSettingsManager * manager,const char * name,XSettingsColor * value)286 xsettings_manager_set_color (XSettingsManager *manager,
287 			     const char       *name,
288 			     XSettingsColor   *value)
289 {
290   XSettingsSetting setting;
291 
292   setting.name = (char *)name;
293   setting.type = XSETTINGS_TYPE_COLOR;
294   setting.data.v_color = *value;
295 
296   return xsettings_manager_set_setting (manager, &setting);
297 }
298 
299 static size_t
setting_length(XSettingsSetting * setting)300 setting_length (XSettingsSetting *setting)
301 {
302   size_t length = 8;	/* type + pad + name-len + last-change-serial */
303   length += XSETTINGS_PAD (strlen (setting->name), 4);
304 
305   switch (setting->type)
306     {
307     case XSETTINGS_TYPE_INT:
308       length += 4;
309       break;
310     case XSETTINGS_TYPE_STRING:
311       length += 4 + XSETTINGS_PAD (strlen (setting->data.v_string), 4);
312       break;
313     case XSETTINGS_TYPE_COLOR:
314       length += 8;
315       break;
316     }
317 
318   return length;
319 }
320 
321 static void
setting_store(XSettingsSetting * setting,XSettingsBuffer * buffer)322 setting_store (XSettingsSetting *setting,
323 	       XSettingsBuffer *buffer)
324 {
325   size_t string_len;
326   size_t length;
327 
328   *(buffer->pos++) = setting->type;
329   *(buffer->pos++) = 0;
330 
331   string_len = strlen (setting->name);
332   *(CARD16 *)(buffer->pos) = string_len;
333   buffer->pos += 2;
334 
335   length = XSETTINGS_PAD (string_len, 4);
336   memcpy (buffer->pos, setting->name, string_len);
337   length -= string_len;
338   buffer->pos += string_len;
339 
340   while (length > 0)
341     {
342       *(buffer->pos++) = 0;
343       length--;
344     }
345 
346   *(CARD32 *)(buffer->pos) = setting->last_change_serial;
347   buffer->pos += 4;
348 
349   switch (setting->type)
350     {
351     case XSETTINGS_TYPE_INT:
352       *(CARD32 *)(buffer->pos) = setting->data.v_int;
353       buffer->pos += 4;
354       break;
355     case XSETTINGS_TYPE_STRING:
356       string_len = strlen (setting->data.v_string);
357       *(CARD32 *)(buffer->pos) = string_len;
358       buffer->pos += 4;
359 
360       length = XSETTINGS_PAD (string_len, 4);
361       memcpy (buffer->pos, setting->data.v_string, string_len);
362       length -= string_len;
363       buffer->pos += string_len;
364 
365       while (length > 0)
366 	{
367 	  *(buffer->pos++) = 0;
368 	  length--;
369 	}
370       break;
371     case XSETTINGS_TYPE_COLOR:
372       *(CARD16 *)(buffer->pos) = setting->data.v_color.red;
373       *(CARD16 *)(buffer->pos + 2) = setting->data.v_color.green;
374       *(CARD16 *)(buffer->pos + 4) = setting->data.v_color.blue;
375       *(CARD16 *)(buffer->pos + 6) = setting->data.v_color.alpha;
376       buffer->pos += 8;
377       break;
378     }
379 }
380 
381 XSettingsResult
xsettings_manager_notify(XSettingsManager * manager)382 xsettings_manager_notify (XSettingsManager *manager)
383 {
384   XSettingsBuffer buffer;
385   XSettingsList *iter;
386   int n_settings = 0;
387 
388   buffer.len = 12;		/* byte-order + pad + SERIAL + N_SETTINGS */
389 
390   iter = settings;
391   while (iter)
392     {
393       buffer.len += setting_length (iter->setting);
394       n_settings++;
395       iter = iter->next;
396     }
397 
398   buffer.data = buffer.pos = malloc (buffer.len);
399   if (!buffer.data)
400     return XSETTINGS_NO_MEM;
401 
402   *buffer.pos = xsettings_byte_order ();
403 
404   buffer.pos += 4;
405   *(CARD32 *)buffer.pos = manager->serial++;
406   buffer.pos += 4;
407   *(CARD32 *)buffer.pos = n_settings;
408   buffer.pos += 4;
409 
410   iter = settings;
411   while (iter)
412     {
413       setting_store (iter->setting, &buffer);
414       iter = iter->next;
415     }
416 
417   XChangeProperty (manager->display, manager->window,
418 		   manager->xsettings_atom, manager->xsettings_atom,
419 		   8, PropModeReplace, buffer.data, buffer.len);
420 
421   free (buffer.data);
422 
423   return XSETTINGS_SUCCESS;
424 }
425 
426