~elementary-os/elementaryos/os-patch-mutter-bionic

« back to all changes in this revision

Viewing changes to clutter/clutter/x11/xsettings/xsettings-client.c

  • Committer: RabbitBot
  • Date: 2018-04-11 14:49:36 UTC
  • Revision ID: rabbitbot@elementary.io-20180411144936-hgymqa9d8d1xfpbh
Initial import, version 3.28.0-2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright © 2001, 2007 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 "clutter-build-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, (unsigned 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
    {
 
445
      if (!client->watch (client->manager_window, True, 
 
446
                          PropertyChangeMask | StructureNotifyMask,
 
447
                          client->cb_data))
 
448
        {
 
449
          /* Inability to watch the window probably means that it was destroyed
 
450
           * after we ungrabbed
 
451
           */
 
452
          client->manager_window = None;
 
453
          return;
 
454
        }
 
455
    }
 
456
      
 
457
  
 
458
  read_settings (client);
 
459
}
 
460
 
 
461
XSettingsClient *
 
462
xsettings_client_new (Display             *display,
 
463
                      int                  screen,
 
464
                      XSettingsNotifyFunc  notify,
 
465
                      XSettingsWatchFunc   watch,
 
466
                      void                *cb_data)
 
467
{
 
468
  return xsettings_client_new_with_grab_funcs (display, screen, notify, watch, cb_data,
 
469
                                               NULL, NULL);
 
470
}
 
471
 
 
472
XSettingsClient *
 
473
xsettings_client_new_with_grab_funcs (Display             *display,
 
474
                                      int                  screen,
 
475
                                      XSettingsNotifyFunc  notify,
 
476
                                      XSettingsWatchFunc   watch,
 
477
                                      void                *cb_data,
 
478
                                      XSettingsGrabFunc    grab,
 
479
                                      XSettingsGrabFunc    ungrab)
 
480
{
 
481
  XSettingsClient *client;
 
482
  char buffer[256];
 
483
  char *atom_names[3];
 
484
  Atom atoms[3];
 
485
  
 
486
  client = malloc (sizeof *client);
 
487
  if (!client)
 
488
    return NULL;
 
489
 
 
490
  client->display = display;
 
491
  client->screen = screen;
 
492
  client->notify = notify;
 
493
  client->watch = watch;
 
494
  client->cb_data = cb_data;
 
495
  client->grab = grab;
 
496
  client->ungrab = ungrab;
 
497
  
 
498
  client->manager_window = None;
 
499
  client->settings = NULL;
 
500
 
 
501
  sprintf(buffer, "_XSETTINGS_S%d", screen);
 
502
  atom_names[0] = buffer;
 
503
  atom_names[1] = "_XSETTINGS_SETTINGS";
 
504
  atom_names[2] = "MANAGER";
 
505
 
 
506
#ifdef HAVE_XINTERNATOMS
 
507
  XInternAtoms (display, atom_names, 3, False, atoms);
 
508
#else
 
509
  atoms[0] = XInternAtom (display, atom_names[0], False);
 
510
  atoms[1] = XInternAtom (display, atom_names[1], False);
 
511
  atoms[2] = XInternAtom (display, atom_names[2], False);
 
512
#endif
 
513
  
 
514
  client->selection_atom = atoms[0];
 
515
  client->xsettings_atom = atoms[1];
 
516
  client->manager_atom = atoms[2];
 
517
 
 
518
  /* Select on StructureNotify so we get MANAGER events
 
519
   */
 
520
  add_events (display, RootWindow (display, screen), StructureNotifyMask);
 
521
 
 
522
  if (client->watch)
 
523
    client->watch (RootWindow (display, screen), True, StructureNotifyMask,
 
524
                   client->cb_data);
 
525
 
 
526
  check_manager_window (client);
 
527
 
 
528
  return client;
 
529
}
 
530
 
 
531
 
 
532
void
 
533
xsettings_client_set_grab_func   (XSettingsClient      *client,
 
534
                                  XSettingsGrabFunc     grab)
 
535
{
 
536
  client->grab = grab;
 
537
}
 
538
 
 
539
void
 
540
xsettings_client_set_ungrab_func (XSettingsClient      *client,
 
541
                                  XSettingsGrabFunc     ungrab)
 
542
{
 
543
  client->ungrab = ungrab;
 
544
}
 
545
 
 
546
void
 
547
xsettings_client_destroy (XSettingsClient *client)
 
548
{
 
549
  if (client->watch)
 
550
    client->watch (RootWindow (client->display, client->screen),
 
551
                   False, 0, client->cb_data);
 
552
  if (client->manager_window && client->watch)
 
553
    client->watch (client->manager_window, False, 0, client->cb_data);
 
554
  
 
555
  xsettings_list_free (client->settings);
 
556
  free (client);
 
557
}
 
558
 
 
559
XSettingsResult
 
560
xsettings_client_get_setting (XSettingsClient   *client,
 
561
                              const char        *name,
 
562
                              XSettingsSetting **setting)
 
563
{
 
564
  XSettingsSetting *search = xsettings_list_lookup (client->settings, name);
 
565
  if (search)
 
566
    {
 
567
      *setting = xsettings_setting_copy (search);
 
568
      return *setting ? XSETTINGS_SUCCESS : XSETTINGS_NO_MEM;
 
569
    }
 
570
  else
 
571
    return XSETTINGS_NO_ENTRY;
 
572
}
 
573
 
 
574
Bool
 
575
xsettings_client_process_event (XSettingsClient *client,
 
576
                                XEvent          *xev)
 
577
{
 
578
  /* The checks here will not unlikely cause us to reread
 
579
   * the properties from the manager window a number of
 
580
   * times when the manager changes from A->B. But manager changes
 
581
   * are going to be pretty rare.
 
582
   */
 
583
  if (xev->xany.window == RootWindow (client->display, client->screen))
 
584
    {
 
585
      if (xev->xany.type == ClientMessage &&
 
586
          xev->xclient.message_type == client->manager_atom &&
 
587
          xev->xclient.data.l[1] == client->selection_atom)
 
588
        {
 
589
          check_manager_window (client);
 
590
          return True;
 
591
        }
 
592
    }
 
593
  else if (xev->xany.window == client->manager_window)
 
594
    {
 
595
      if (xev->xany.type == DestroyNotify)
 
596
        {
 
597
          check_manager_window (client);
 
598
          /* let GDK do its cleanup */
 
599
          return False; 
 
600
        }
 
601
      else if (xev->xany.type == PropertyNotify)
 
602
        {
 
603
          read_settings (client);
 
604
          return True;
 
605
        }
 
606
    }
 
607
  
 
608
  return False;
 
609
}