]> Pileus Git - ~andy/gtk/blob - gdk/x11/xsettings-client.c
Make GDK+ compile with X11R5 (#148032)
[~andy/gtk] / gdk / x11 / xsettings-client.c
1 /*
2  * Copyright © 2001 Red Hat, Inc.
3  *
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.
13  *
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.
20  *
21  * Author:  Owen Taylor, Red Hat, Inc.
22  */
23 #include <config.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <X11/Xlib.h>
30 #include <X11/Xmd.h>            /* For CARD16 */
31
32 #include "xsettings-client.h"
33
34 struct _XSettingsClient
35 {
36   Display *display;
37   int screen;
38   XSettingsNotifyFunc notify;
39   XSettingsWatchFunc watch;
40   void *cb_data;
41
42   XSettingsGrabFunc grab;
43   XSettingsGrabFunc ungrab;
44
45   Window manager_window;
46   Atom manager_atom;
47   Atom selection_atom;
48   Atom xsettings_atom;
49
50   XSettingsList *settings;
51 };
52
53 static void
54 notify_changes (XSettingsClient *client,
55                 XSettingsList   *old_list)
56 {
57   XSettingsList *old_iter = old_list;
58   XSettingsList *new_iter = client->settings;
59
60   if (!client->notify)
61     return;
62
63   while (old_iter || new_iter)
64     {
65       int cmp;
66       
67       if (old_iter && new_iter)
68         cmp = strcmp (old_iter->setting->name, new_iter->setting->name);
69       else if (old_iter)
70         cmp = -1;
71       else
72         cmp = 1;
73
74       if (cmp < 0)
75         {
76           client->notify (old_iter->setting->name,
77                           XSETTINGS_ACTION_DELETED,
78                           NULL,
79                           client->cb_data);
80         }
81       else if (cmp == 0)
82         {
83           if (!xsettings_setting_equal (old_iter->setting,
84                                         new_iter->setting))
85             client->notify (old_iter->setting->name,
86                             XSETTINGS_ACTION_CHANGED,
87                             new_iter->setting,
88                             client->cb_data);
89         }
90       else
91         {
92           client->notify (new_iter->setting->name,
93                           XSETTINGS_ACTION_NEW,
94                           new_iter->setting,
95                           client->cb_data);
96         }
97
98       if (old_iter)
99         old_iter = old_iter->next;
100       if (new_iter)
101         new_iter = new_iter->next;
102     }
103 }
104
105 static int
106 ignore_errors (Display *display, XErrorEvent *event)
107 {
108   return True;
109 }
110
111 static char local_byte_order = '\0';
112
113 #define BYTES_LEFT(buffer) ((buffer)->data + (buffer)->len - (buffer)->pos)
114
115 static XSettingsResult
116 fetch_card16 (XSettingsBuffer *buffer,
117               CARD16          *result)
118 {
119   CARD16 x;
120
121   if (BYTES_LEFT (buffer) < 2)
122     return XSETTINGS_ACCESS;
123
124   x = *(CARD16 *)buffer->pos;
125   buffer->pos += 2;
126   
127   if (buffer->byte_order == local_byte_order)
128     *result = x;
129   else
130     *result = (x << 8) | (x >> 8);
131
132   return XSETTINGS_SUCCESS;
133 }
134
135 static XSettingsResult
136 fetch_ushort (XSettingsBuffer *buffer,
137               unsigned short  *result) 
138 {
139   CARD16 x;
140   XSettingsResult r;  
141
142   r = fetch_card16 (buffer, &x);
143   if (r == XSETTINGS_SUCCESS)
144     *result = x;
145
146   return r;
147 }
148
149 static XSettingsResult
150 fetch_card32 (XSettingsBuffer *buffer,
151               CARD32          *result)
152 {
153   CARD32 x;
154
155   if (BYTES_LEFT (buffer) < 4)
156     return XSETTINGS_ACCESS;
157
158   x = *(CARD32 *)buffer->pos;
159   buffer->pos += 4;
160   
161   if (buffer->byte_order == local_byte_order)
162     *result = x;
163   else
164     *result = (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
165   
166   return XSETTINGS_SUCCESS;
167 }
168
169 static XSettingsResult
170 fetch_card8 (XSettingsBuffer *buffer,
171              CARD8           *result)
172 {
173   if (BYTES_LEFT (buffer) < 1)
174     return XSETTINGS_ACCESS;
175
176   *result = *(CARD8 *)buffer->pos;
177   buffer->pos += 1;
178
179   return XSETTINGS_SUCCESS;
180 }
181
182 #define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1)))
183
184 static XSettingsList *
185 parse_settings (unsigned char *data,
186                 size_t         len)
187 {
188   XSettingsBuffer buffer;
189   XSettingsResult result = XSETTINGS_SUCCESS;
190   XSettingsList *settings = NULL;
191   CARD32 serial;
192   CARD32 n_entries;
193   CARD32 i;
194   XSettingsSetting *setting = NULL;
195   
196   local_byte_order = xsettings_byte_order ();
197
198   buffer.pos = buffer.data = data;
199   buffer.len = len;
200   
201   result = fetch_card8 (&buffer, (char *)&buffer.byte_order);
202   if (buffer.byte_order != MSBFirst &&
203       buffer.byte_order != LSBFirst)
204     {
205       fprintf (stderr, "Invalid byte order in XSETTINGS property\n");
206       result = XSETTINGS_FAILED;
207       goto out;
208     }
209
210   buffer.pos += 3;
211
212   result = fetch_card32 (&buffer, &serial);
213   if (result != XSETTINGS_SUCCESS)
214     goto out;
215
216   result = fetch_card32 (&buffer, &n_entries);
217   if (result != XSETTINGS_SUCCESS)
218     goto out;
219
220   for (i = 0; i < n_entries; i++)
221     {
222       CARD8 type;
223       CARD16 name_len;
224       CARD32 v_int;
225       size_t pad_len;
226       
227       result = fetch_card8 (&buffer, &type);
228       if (result != XSETTINGS_SUCCESS)
229         goto out;
230
231       buffer.pos += 1;
232
233       result = fetch_card16 (&buffer, &name_len);
234       if (result != XSETTINGS_SUCCESS)
235         goto out;
236
237       pad_len = XSETTINGS_PAD(name_len, 4);
238       if (BYTES_LEFT (&buffer) < pad_len)
239         {
240           result = XSETTINGS_ACCESS;
241           goto out;
242         }
243
244       setting = malloc (sizeof *setting);
245       if (!setting)
246         {
247           result = XSETTINGS_NO_MEM;
248           goto out;
249         }
250       setting->type = XSETTINGS_TYPE_INT; /* No allocated memory */
251
252       setting->name = malloc (name_len + 1);
253       if (!setting->name)
254         {
255           result = XSETTINGS_NO_MEM;
256           goto out;
257         }
258
259       memcpy (setting->name, buffer.pos, name_len);
260       setting->name[name_len] = '\0';
261       buffer.pos += pad_len;
262
263       result = fetch_card32 (&buffer, &v_int);
264       if (result != XSETTINGS_SUCCESS)
265         goto out;
266       setting->last_change_serial = v_int;
267
268       switch (type)
269         {
270         case XSETTINGS_TYPE_INT:
271           result = fetch_card32 (&buffer, &v_int);
272           if (result != XSETTINGS_SUCCESS)
273             goto out;
274
275           setting->data.v_int = (INT32)v_int;
276           break;
277         case XSETTINGS_TYPE_STRING:
278           result = fetch_card32 (&buffer, &v_int);
279           if (result != XSETTINGS_SUCCESS)
280             goto out;
281
282           pad_len = XSETTINGS_PAD (v_int, 4);
283           if (v_int + 1 == 0 || /* Guard against wrap-around */
284               BYTES_LEFT (&buffer) < pad_len)
285             {
286               result = XSETTINGS_ACCESS;
287               goto out;
288             }
289
290           setting->data.v_string = malloc (v_int + 1);
291           if (!setting->data.v_string)
292             {
293               result = XSETTINGS_NO_MEM;
294               goto out;
295             }
296           
297           memcpy (setting->data.v_string, buffer.pos, v_int);
298           setting->data.v_string[v_int] = '\0';
299           buffer.pos += pad_len;
300
301           break;
302         case XSETTINGS_TYPE_COLOR:
303           result = fetch_ushort (&buffer, &setting->data.v_color.red);
304           if (result != XSETTINGS_SUCCESS)
305             goto out;
306           result = fetch_ushort (&buffer, &setting->data.v_color.green);
307           if (result != XSETTINGS_SUCCESS)
308             goto out;
309           result = fetch_ushort (&buffer, &setting->data.v_color.blue);
310           if (result != XSETTINGS_SUCCESS)
311             goto out;
312           result = fetch_ushort (&buffer, &setting->data.v_color.alpha);
313           if (result != XSETTINGS_SUCCESS)
314             goto out;
315
316           break;
317         default:
318           /* Quietly ignore unknown types */
319           break;
320         }
321
322       setting->type = type;
323
324       result = xsettings_list_insert (&settings, setting);
325       if (result != XSETTINGS_SUCCESS)
326         goto out;
327
328       setting = NULL;
329     }
330
331  out:
332
333   if (result != XSETTINGS_SUCCESS)
334     {
335       switch (result)
336         {
337         case XSETTINGS_NO_MEM:
338           fprintf(stderr, "Out of memory reading XSETTINGS property\n");
339           break;
340         case XSETTINGS_ACCESS:
341           fprintf(stderr, "Invalid XSETTINGS property (read off end)\n");
342           break;
343         case XSETTINGS_DUPLICATE_ENTRY:
344           fprintf (stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name);
345         case XSETTINGS_FAILED:
346         case XSETTINGS_SUCCESS:
347         case XSETTINGS_NO_ENTRY:
348           break;
349         }
350
351       if (setting)
352         xsettings_setting_free (setting);
353
354       xsettings_list_free (settings);
355       settings = NULL;
356
357     }
358
359   return settings;
360 }
361
362 static void
363 read_settings (XSettingsClient *client)
364 {
365   Atom type;
366   int format;
367   unsigned long n_items;
368   unsigned long bytes_after;
369   unsigned char *data;
370   int result;
371
372   int (*old_handler) (Display *, XErrorEvent *);
373   
374   XSettingsList *old_list = client->settings;
375
376   client->settings = NULL;
377
378   if (client->manager_window)
379     {
380       old_handler = XSetErrorHandler (ignore_errors);
381       result = XGetWindowProperty (client->display, client->manager_window,
382                                    client->xsettings_atom, 0, LONG_MAX,
383                                    False, client->xsettings_atom,
384                                    &type, &format, &n_items, &bytes_after, &data);
385       XSetErrorHandler (old_handler);
386       
387       if (result == Success && type != None)
388         {
389           if (type != client->xsettings_atom)
390             {
391               fprintf (stderr, "Invalid type for XSETTINGS property");
392             }
393           else if (format != 8)
394             {
395               fprintf (stderr, "Invalid format for XSETTINGS property %d", format);
396             }
397           else
398             client->settings = parse_settings (data, n_items);
399           
400           XFree (data);
401         }
402     }
403
404   notify_changes (client, old_list);
405   xsettings_list_free (old_list);
406 }
407
408 static void
409 add_events (Display *display,
410             Window   window,
411             long     mask)
412 {
413   XWindowAttributes attr;
414
415   XGetWindowAttributes (display, window, &attr);
416   XSelectInput (display, window, attr.your_event_mask | mask);
417 }
418
419 static void
420 check_manager_window (XSettingsClient *client)
421 {
422   if (client->manager_window && client->watch)
423     client->watch (client->manager_window, False, 0, client->cb_data);
424
425   if (client->grab)
426     client->grab (client->display);
427   else
428     XGrabServer (client->display);
429
430   client->manager_window = XGetSelectionOwner (client->display,
431                                                client->selection_atom);
432   if (client->manager_window)
433     XSelectInput (client->display, client->manager_window,
434                   PropertyChangeMask | StructureNotifyMask);
435
436   if (client->ungrab)
437     client->ungrab (client->display);
438   else
439     XUngrabServer (client->display);
440   
441   XFlush (client->display);
442
443   if (client->manager_window && client->watch)
444     client->watch (client->manager_window, True, 
445                    PropertyChangeMask | StructureNotifyMask,
446                    client->cb_data);
447   
448   read_settings (client);
449 }
450
451 XSettingsClient *
452 xsettings_client_new (Display             *display,
453                       int                  screen,
454                       XSettingsNotifyFunc  notify,
455                       XSettingsWatchFunc   watch,
456                       void                *cb_data)
457 {
458   XSettingsClient *client;
459   char buffer[256];
460   char *atom_names[3];
461   Atom atoms[3];
462   
463   client = malloc (sizeof *client);
464   if (!client)
465     return NULL;
466
467   client->display = display;
468   client->screen = screen;
469   client->notify = notify;
470   client->watch = watch;
471   client->cb_data = cb_data;
472   client->grab = NULL;
473   client->ungrab = NULL;
474   
475   client->manager_window = None;
476   client->settings = NULL;
477
478   sprintf(buffer, "_XSETTINGS_S%d", screen);
479   atom_names[0] = buffer;
480   atom_names[1] = "_XSETTINGS_SETTINGS";
481   atom_names[2] = "MANAGER";
482
483 #ifdef HAVE_XINTERNATOMS
484   XInternAtoms (display, atom_names, 3, False, atoms);
485 #else
486   atoms[0] = XInternAtom (display, atom_names[0], False);
487   atoms[1] = XInternAtom (display, atom_names[1], False);
488   atoms[2] = XInternAtom (display, atom_names[2], False);
489 #endif
490   
491   client->selection_atom = atoms[0];
492   client->xsettings_atom = atoms[1];
493   client->manager_atom = atoms[2];
494
495   /* Select on StructureNotify so we get MANAGER events
496    */
497   add_events (display, RootWindow (display, screen), StructureNotifyMask);
498
499   if (client->watch)
500     client->watch (RootWindow (display, screen), True, StructureNotifyMask,
501                    client->cb_data);
502
503   check_manager_window (client);
504
505   return client;
506 }
507
508 void
509 xsettings_client_set_grab_func   (XSettingsClient      *client,
510                                   XSettingsGrabFunc     grab)
511 {
512   client->grab = grab;
513 }
514
515 void
516 xsettings_client_set_ungrab_func (XSettingsClient      *client,
517                                   XSettingsGrabFunc     ungrab)
518 {
519   client->ungrab = ungrab;
520 }
521
522 void
523 xsettings_client_destroy (XSettingsClient *client)
524 {
525   if (client->watch)
526     client->watch (RootWindow (client->display, client->screen),
527                    False, 0, client->cb_data);
528   if (client->manager_window && client->watch)
529     client->watch (client->manager_window, False, 0, client->cb_data);
530   
531   xsettings_list_free (client->settings);
532   free (client);
533 }
534
535 XSettingsResult
536 xsettings_client_get_setting (XSettingsClient   *client,
537                               const char        *name,
538                               XSettingsSetting **setting)
539 {
540   XSettingsSetting *search = xsettings_list_lookup (client->settings, name);
541   if (search)
542     {
543       *setting = xsettings_setting_copy (search);
544       return *setting ? XSETTINGS_SUCCESS : XSETTINGS_NO_MEM;
545     }
546   else
547     return XSETTINGS_NO_ENTRY;
548 }
549
550 Bool
551 xsettings_client_process_event (XSettingsClient *client,
552                                 XEvent          *xev)
553 {
554   /* The checks here will not unlikely cause us to reread
555    * the properties from the manager window a number of
556    * times when the manager changes from A->B. But manager changes
557    * are going to be pretty rare.
558    */
559   if (xev->xany.window == RootWindow (client->display, client->screen))
560     {
561       if (xev->xany.type == ClientMessage &&
562           xev->xclient.message_type == client->manager_atom &&
563           xev->xclient.data.l[1] == client->selection_atom)
564         {
565           check_manager_window (client);
566           return True;
567         }
568     }
569   else if (xev->xany.window == client->manager_window)
570     {
571       if (xev->xany.type == DestroyNotify)
572         {
573           check_manager_window (client);
574           return True;
575         }
576       else if (xev->xany.type == PropertyNotify)
577         {
578           read_settings (client);
579           return True;
580         }
581     }
582   
583   return False;
584 }