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