2 * Copyright © 2001, 2007 Red Hat, Inc.
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Red Hat not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. Red Hat makes no representations about the
11 * suitability of this software for any purpose. It is provided "as is"
12 * without express or implied warranty.
14 * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
16 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
18 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
19 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 * Author: Owen Taylor, Red Hat, Inc.
26 #include "xsettings-client.h"
28 #include <gdk/x11/gdkx11display.h>
29 #include <gdk/x11/gdkx11screen.h>
30 #include <gdk/x11/gdkx11window.h>
32 #include <gdkinternals.h>
39 #include <X11/Xmd.h> /* For CARD16 */
41 typedef struct _XSettingsBuffer XSettingsBuffer;
49 XSETTINGS_DUPLICATE_ENTRY
52 struct _XSettingsBuffer
60 struct _XSettingsClient
64 XSettingsNotifyFunc notify;
65 XSettingsWatchFunc watch;
68 Window manager_window;
73 GHashTable *settings; /* string => XSettingsSetting */
77 notify_changes (XSettingsClient *client,
81 XSettingsSetting *setting, *old_setting;
86 if (client->settings != NULL)
88 g_hash_table_iter_init (&iter, client->settings);
89 while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &setting))
91 old_setting = old_list ? g_hash_table_lookup (old_list, setting->name) : NULL;
93 if (old_setting == NULL)
94 client->notify (setting->name, XSETTINGS_ACTION_NEW, setting, client->cb_data);
95 else if (!xsettings_setting_equal (setting, old_setting))
96 client->notify (setting->name, XSETTINGS_ACTION_CHANGED, setting, client->cb_data);
98 /* remove setting from old_list */
99 if (old_setting != NULL)
100 g_hash_table_remove (old_list, setting->name);
104 if (old_list != NULL)
106 /* old_list now contains only deleted settings */
107 g_hash_table_iter_init (&iter, old_list);
108 while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &old_setting))
109 client->notify (old_setting->name, XSETTINGS_ACTION_DELETED, NULL, client->cb_data);
114 ignore_errors (Display *display, XErrorEvent *event)
119 #define BYTES_LEFT(buffer) ((buffer)->data + (buffer)->len - (buffer)->pos)
121 static XSettingsResult
122 fetch_card16 (XSettingsBuffer *buffer,
127 if (BYTES_LEFT (buffer) < 2)
128 return XSETTINGS_ACCESS;
130 x = *(CARD16 *)buffer->pos;
133 if (buffer->byte_order == MSBFirst)
134 *result = GUINT16_FROM_BE (x);
136 *result = GUINT16_FROM_LE (x);
138 return XSETTINGS_SUCCESS;
141 static XSettingsResult
142 fetch_ushort (XSettingsBuffer *buffer,
143 unsigned short *result)
148 r = fetch_card16 (buffer, &x);
149 if (r == XSETTINGS_SUCCESS)
155 static XSettingsResult
156 fetch_card32 (XSettingsBuffer *buffer,
161 if (BYTES_LEFT (buffer) < 4)
162 return XSETTINGS_ACCESS;
164 x = *(CARD32 *)buffer->pos;
167 if (buffer->byte_order == MSBFirst)
168 *result = GUINT32_FROM_BE (x);
170 *result = GUINT32_FROM_LE (x);
172 return XSETTINGS_SUCCESS;
175 static XSettingsResult
176 fetch_card8 (XSettingsBuffer *buffer,
179 if (BYTES_LEFT (buffer) < 1)
180 return XSETTINGS_ACCESS;
182 *result = *(CARD8 *)buffer->pos;
185 return XSETTINGS_SUCCESS;
188 #define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1)))
190 static XSettingsResult
191 fetch_string (XSettingsBuffer *buffer,
197 pad_len = XSETTINGS_PAD (length, 4);
198 if (pad_len < length /* guard against overflow */
199 || BYTES_LEFT (buffer) < pad_len)
200 return XSETTINGS_ACCESS;
202 *result = g_strndup ((char *) buffer->pos, length);
203 buffer->pos += pad_len;
205 return XSETTINGS_SUCCESS;
209 parse_settings (unsigned char *data,
212 XSettingsBuffer buffer;
213 XSettingsResult result = XSETTINGS_SUCCESS;
214 GHashTable *settings = NULL;
218 XSettingsSetting *setting = NULL;
220 buffer.pos = buffer.data = data;
223 result = fetch_card8 (&buffer, (unsigned char *)&buffer.byte_order);
224 if (buffer.byte_order != MSBFirst &&
225 buffer.byte_order != LSBFirst)
227 fprintf (stderr, "Invalid byte order in XSETTINGS property\n");
228 result = XSETTINGS_FAILED;
234 result = fetch_card32 (&buffer, &serial);
235 if (result != XSETTINGS_SUCCESS)
238 result = fetch_card32 (&buffer, &n_entries);
239 if (result != XSETTINGS_SUCCESS)
242 GDK_NOTE(SETTINGS, g_print("reading %u settings (serial %u byte order %u)\n", n_entries, serial, buffer.byte_order));
244 for (i = 0; i < n_entries; i++)
250 result = fetch_card8 (&buffer, &type);
251 if (result != XSETTINGS_SUCCESS)
256 result = fetch_card16 (&buffer, &name_len);
257 if (result != XSETTINGS_SUCCESS)
260 setting = g_new (XSettingsSetting, 1);
261 setting->type = XSETTINGS_TYPE_INT; /* No allocated memory */
263 setting->name = NULL;
264 result = fetch_string (&buffer, name_len, &setting->name);
265 if (result != XSETTINGS_SUCCESS)
268 /* last change serial (we ignore it) */
269 result = fetch_card32 (&buffer, &v_int);
270 if (result != XSETTINGS_SUCCESS)
275 case XSETTINGS_TYPE_INT:
276 result = fetch_card32 (&buffer, &v_int);
277 if (result != XSETTINGS_SUCCESS)
280 setting->data.v_int = (INT32)v_int;
281 GDK_NOTE(SETTINGS, g_print(" %s = %d\n", setting->name, (gint) setting->data.v_int));
283 case XSETTINGS_TYPE_STRING:
284 result = fetch_card32 (&buffer, &v_int);
285 if (result != XSETTINGS_SUCCESS)
288 result = fetch_string (&buffer, v_int, &setting->data.v_string);
289 if (result != XSETTINGS_SUCCESS)
292 GDK_NOTE(SETTINGS, g_print(" %s = \"%s\"\n", setting->name, setting->data.v_string));
294 case XSETTINGS_TYPE_COLOR:
295 result = fetch_ushort (&buffer, &setting->data.v_color.red);
296 if (result != XSETTINGS_SUCCESS)
298 result = fetch_ushort (&buffer, &setting->data.v_color.green);
299 if (result != XSETTINGS_SUCCESS)
301 result = fetch_ushort (&buffer, &setting->data.v_color.blue);
302 if (result != XSETTINGS_SUCCESS)
304 result = fetch_ushort (&buffer, &setting->data.v_color.alpha);
305 if (result != XSETTINGS_SUCCESS)
308 GDK_NOTE(SETTINGS, g_print(" %s = #%02X%02X%02X%02X\n", setting->name,
309 setting->data.v_color.alpha, setting->data.v_color.red,
310 setting->data.v_color.green, setting->data.v_color.blue));
313 /* Quietly ignore unknown types */
314 GDK_NOTE(SETTINGS, g_print(" %s = ignored (unknown type %u)\n", setting->name, type));
318 setting->type = type;
320 if (settings == NULL)
321 settings = g_hash_table_new_full (g_str_hash, g_str_equal,
323 (GDestroyNotify) xsettings_setting_free);
325 if (g_hash_table_lookup (settings, setting->name) != NULL)
327 result = XSETTINGS_DUPLICATE_ENTRY;
331 g_hash_table_insert (settings, setting->name, setting);
337 if (result != XSETTINGS_SUCCESS)
341 case XSETTINGS_ACCESS:
342 fprintf(stderr, "Invalid XSETTINGS property (read off end)\n");
344 case XSETTINGS_DUPLICATE_ENTRY:
345 fprintf (stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name);
346 case XSETTINGS_FAILED:
347 case XSETTINGS_SUCCESS:
348 case XSETTINGS_NO_ENTRY:
353 xsettings_setting_free (setting);
356 g_hash_table_unref (settings);
364 read_settings (XSettingsClient *client)
368 unsigned long n_items;
369 unsigned long bytes_after;
373 int (*old_handler) (Display *, XErrorEvent *);
375 GHashTable *old_list = client->settings;
377 client->settings = NULL;
379 if (client->manager_window)
381 old_handler = XSetErrorHandler (ignore_errors);
382 result = XGetWindowProperty (client->display, client->manager_window,
383 client->xsettings_atom, 0, LONG_MAX,
384 False, client->xsettings_atom,
385 &type, &format, &n_items, &bytes_after, &data);
386 XSetErrorHandler (old_handler);
388 if (result == Success && type != None)
390 if (type != client->xsettings_atom)
392 fprintf (stderr, "Invalid type for XSETTINGS property");
394 else if (format != 8)
396 fprintf (stderr, "Invalid format for XSETTINGS property %d", format);
399 client->settings = parse_settings (data, n_items);
405 notify_changes (client, old_list);
407 g_hash_table_unref (old_list);
411 add_events (Display *display,
415 XWindowAttributes attr;
417 XGetWindowAttributes (display, window, &attr);
418 XSelectInput (display, window, attr.your_event_mask | mask);
422 check_manager_window (XSettingsClient *client)
424 if (client->manager_window && client->watch)
425 client->watch (client->manager_window, False, 0, client->cb_data);
427 gdk_x11_display_grab (gdk_screen_get_display (client->screen));
429 client->manager_window = XGetSelectionOwner (client->display,
430 client->selection_atom);
431 if (client->manager_window)
432 XSelectInput (client->display, client->manager_window,
433 PropertyChangeMask | StructureNotifyMask);
435 gdk_x11_display_ungrab (gdk_screen_get_display (client->screen));
437 XFlush (client->display);
439 if (client->manager_window && client->watch)
441 if (!client->watch (client->manager_window, True,
442 PropertyChangeMask | StructureNotifyMask,
445 /* Inability to watch the window probably means that it was destroyed
448 client->manager_window = None;
454 read_settings (client);
458 xsettings_client_new (GdkScreen *screen,
459 XSettingsNotifyFunc notify,
460 XSettingsWatchFunc watch,
463 XSettingsClient *client;
468 client = g_new (XSettingsClient, 1);
472 client->screen = screen;
473 client->display = gdk_x11_display_get_xdisplay (gdk_screen_get_display (screen));
474 client->notify = notify;
475 client->watch = watch;
476 client->cb_data = cb_data;
477 client->manager_window = None;
478 client->settings = NULL;
480 sprintf(buffer, "_XSETTINGS_S%d", gdk_x11_screen_get_screen_number (screen));
481 atom_names[0] = buffer;
482 atom_names[1] = "_XSETTINGS_SETTINGS";
483 atom_names[2] = "MANAGER";
485 XInternAtoms (client->display, atom_names, 3, False, atoms);
487 client->selection_atom = atoms[0];
488 client->xsettings_atom = atoms[1];
489 client->manager_atom = atoms[2];
491 /* Select on StructureNotify so we get MANAGER events
493 add_events (client->display, gdk_x11_window_get_xid (gdk_screen_get_root_window (screen)), StructureNotifyMask);
496 client->watch (gdk_x11_window_get_xid (gdk_screen_get_root_window (screen)), True, StructureNotifyMask,
499 check_manager_window (client);
505 xsettings_client_destroy (XSettingsClient *client)
508 client->watch (gdk_x11_window_get_xid (gdk_screen_get_root_window (client->screen)),
509 False, 0, client->cb_data);
510 if (client->manager_window && client->watch)
511 client->watch (client->manager_window, False, 0, client->cb_data);
513 if (client->settings)
514 g_hash_table_unref (client->settings);
518 const XSettingsSetting *
519 xsettings_client_get_setting (XSettingsClient *client,
522 return g_hash_table_lookup (client->settings, name);
526 xsettings_client_process_event (XSettingsClient *client,
529 /* The checks here will not unlikely cause us to reread
530 * the properties from the manager window a number of
531 * times when the manager changes from A->B. But manager changes
532 * are going to be pretty rare.
534 if (xev->xany.window == gdk_x11_window_get_xid (gdk_screen_get_root_window (client->screen)))
536 if (xev->xany.type == ClientMessage &&
537 xev->xclient.message_type == client->manager_atom &&
538 xev->xclient.data.l[1] == client->selection_atom)
540 check_manager_window (client);
544 else if (xev->xany.window == client->manager_window)
546 if (xev->xany.type == DestroyNotify)
548 check_manager_window (client);
549 /* let GDK do its cleanup */
552 else if (xev->xany.type == PropertyNotify)
554 read_settings (client);
563 xsettings_setting_equal (XSettingsSetting *setting_a,
564 XSettingsSetting *setting_b)
566 if (setting_a->type != setting_b->type)
569 if (strcmp (setting_a->name, setting_b->name) != 0)
572 switch (setting_a->type)
574 case XSETTINGS_TYPE_INT:
575 return setting_a->data.v_int == setting_b->data.v_int;
576 case XSETTINGS_TYPE_COLOR:
577 return (setting_a->data.v_color.red == setting_b->data.v_color.red &&
578 setting_a->data.v_color.green == setting_b->data.v_color.green &&
579 setting_a->data.v_color.blue == setting_b->data.v_color.blue &&
580 setting_a->data.v_color.alpha == setting_b->data.v_color.alpha);
581 case XSETTINGS_TYPE_STRING:
582 return strcmp (setting_a->data.v_string, setting_b->data.v_string) == 0;
589 xsettings_setting_free (XSettingsSetting *setting)
591 if (setting->type == XSETTINGS_TYPE_STRING)
592 g_free (setting->data.v_string);
595 g_free (setting->name);