Atlas - xsettings-client.c

Home / ext / SDL / src / video / x11 Lines: 5 | Size: 21339 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 * Copyright © 2001, 2007 Red Hat, Inc. 3 * Copyright 2024 Igalia S.L. 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 25#include "SDL_internal.h" 26 27#ifdef SDL_VIDEO_DRIVER_X11 28 29#include "SDL_x11video.h" 30 31#include <limits.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35 36#include "xsettings-client.h" 37 38struct _XSettingsClient 39{ 40 Display *display; 41 int screen; 42 XSettingsNotifyFunc notify; 43 XSettingsWatchFunc watch; 44 void *cb_data; 45 46 XSettingsGrabFunc grab; 47 XSettingsGrabFunc ungrab; 48 49 Window manager_window; 50 Atom manager_atom; 51 Atom selection_atom; 52 Atom xsettings_atom; 53 54 XSettingsList *settings; 55}; 56 57static void 58notify_changes (XSettingsClient *client, 59 XSettingsList *old_list) 60{ 61 XSettingsList *old_iter = old_list; 62 XSettingsList *new_iter = client->settings; 63 64 if (!client->notify) 65 return; 66 67 while (old_iter || new_iter) 68 { 69 int cmp; 70 71 if (old_iter && new_iter) 72 cmp = strcmp (old_iter->setting->name, new_iter->setting->name); 73 else if (old_iter) 74 cmp = -1; 75 else 76 cmp = 1; 77 78 if (cmp < 0) 79 { 80 client->notify (old_iter->setting->name, 81 XSETTINGS_ACTION_DELETED, 82 NULL, 83 client->cb_data); 84 } 85 else if (cmp == 0) 86 { 87 if (!xsettings_setting_equal (old_iter->setting, 88 new_iter->setting)) 89 client->notify (old_iter->setting->name, 90 XSETTINGS_ACTION_CHANGED, 91 new_iter->setting, 92 client->cb_data); 93 } 94 else 95 { 96 client->notify (new_iter->setting->name, 97 XSETTINGS_ACTION_NEW, 98 new_iter->setting, 99 client->cb_data); 100 } 101 102 if (old_iter) 103 old_iter = old_iter->next; 104 if (new_iter) 105 new_iter = new_iter->next; 106 } 107} 108 109static int 110ignore_errors (Display *display, XErrorEvent *event) 111{ 112 return True; 113} 114 115static char local_byte_order = '\0'; 116 117#define BYTES_LEFT(buffer) ((buffer)->data + (buffer)->len - (buffer)->pos) 118 119static XSettingsResult 120fetch_card16 (XSettingsBuffer *buffer, 121 CARD16 *result) 122{ 123 CARD16 x; 124 125 if (BYTES_LEFT (buffer) < 2) 126 return XSETTINGS_ACCESS; 127 128 x = *(CARD16 *)buffer->pos; 129 buffer->pos += 2; 130 131 if (buffer->byte_order == local_byte_order) 132 *result = x; 133 else 134 *result = (x << 8) | (x >> 8); 135 136 return XSETTINGS_SUCCESS; 137} 138 139static XSettingsResult 140fetch_ushort (XSettingsBuffer *buffer, 141 unsigned short *result) 142{ 143 CARD16 x; 144 XSettingsResult r; 145 146 r = fetch_card16 (buffer, &x); 147 if (r == XSETTINGS_SUCCESS) 148 *result = x; 149 150 return r; 151} 152 153static XSettingsResult 154fetch_card32 (XSettingsBuffer *buffer, 155 CARD32 *result) 156{ 157 CARD32 x; 158 159 if (BYTES_LEFT (buffer) < 4) 160 return XSETTINGS_ACCESS; 161 162 x = *(CARD32 *)buffer->pos; 163 buffer->pos += 4; 164 165 if (buffer->byte_order == local_byte_order) 166 *result = x; 167 else 168 *result = (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24); 169 170 return XSETTINGS_SUCCESS; 171} 172 173static XSettingsResult 174fetch_card8 (XSettingsBuffer *buffer, 175 CARD8 *result) 176{ 177 if (BYTES_LEFT (buffer) < 1) 178 return XSETTINGS_ACCESS; 179 180 *result = *(CARD8 *)buffer->pos; 181 buffer->pos += 1; 182 183 return XSETTINGS_SUCCESS; 184} 185 186#define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1))) 187 188static XSettingsList * 189parse_settings (unsigned char *data, 190 size_t len) 191{ 192 XSettingsBuffer buffer; 193 XSettingsResult result = XSETTINGS_SUCCESS; 194 XSettingsList *settings = NULL; 195 CARD32 serial; 196 CARD32 n_entries; 197 CARD32 i; 198 XSettingsSetting *setting = NULL; 199 char buffer_byte_order = '\0'; 200 201 local_byte_order = xsettings_byte_order (); 202 203 buffer.pos = buffer.data = data; 204 buffer.len = len; 205 buffer.byte_order = '\0'; 206 207 result = fetch_card8 (&buffer, (unsigned char *) &buffer_byte_order); 208 if (buffer_byte_order != MSBFirst && 209 buffer_byte_order != LSBFirst) 210 { 211 fprintf (stderr, "Invalid byte order in XSETTINGS property\n"); 212 result = XSETTINGS_FAILED; 213 goto out; 214 } 215 216 buffer.byte_order = buffer_byte_order; 217 buffer.pos += 3; 218 219 result = fetch_card32 (&buffer, &serial); 220 if (result != XSETTINGS_SUCCESS) 221 goto out; 222 223 result = fetch_card32 (&buffer, &n_entries); 224 if (result != XSETTINGS_SUCCESS) 225 goto out; 226 227 for (i = 0; i < n_entries; i++) 228 { 229 CARD8 type; 230 CARD16 name_len; 231 CARD32 v_int; 232 size_t pad_len; 233 234 result = fetch_card8 (&buffer, &type); 235 if (result != XSETTINGS_SUCCESS) 236 goto out; 237 238 buffer.pos += 1; 239 240 result = fetch_card16 (&buffer, &name_len); 241 if (result != XSETTINGS_SUCCESS) 242 goto out; 243 244 pad_len = XSETTINGS_PAD(name_len, 4); 245 if (BYTES_LEFT (&buffer) < pad_len) 246 { 247 result = XSETTINGS_ACCESS; 248 goto out; 249 } 250 251 setting = malloc (sizeof *setting); 252 if (!setting) 253 { 254 result = XSETTINGS_NO_MEM; 255 goto out; 256 } 257 setting->type = XSETTINGS_TYPE_INT; /* No allocated memory */ 258 259 setting->name = malloc (name_len + 1); 260 if (!setting->name) 261 { 262 result = XSETTINGS_NO_MEM; 263 goto out; 264 } 265 266 memcpy (setting->name, buffer.pos, name_len); 267 setting->name[name_len] = '\0'; 268 buffer.pos += pad_len; 269 270 result = fetch_card32 (&buffer, &v_int); 271 if (result != XSETTINGS_SUCCESS) 272 goto out; 273 setting->last_change_serial = v_int; 274 275 switch (type) 276 { 277 case XSETTINGS_TYPE_INT: 278 result = fetch_card32 (&buffer, &v_int); 279 if (result != XSETTINGS_SUCCESS) 280 goto out; 281 282 setting->data.v_int = (INT32)v_int; 283 break; 284 case XSETTINGS_TYPE_STRING: 285 result = fetch_card32 (&buffer, &v_int); 286 if (result != XSETTINGS_SUCCESS) 287 goto out; 288 289 pad_len = XSETTINGS_PAD (v_int, 4); 290 if (v_int + 1 == 0 || /* Guard against wrap-around */ 291 BYTES_LEFT (&buffer) < pad_len) 292 { 293 result = XSETTINGS_ACCESS; 294 goto out; 295 } 296 297 setting->data.v_string = malloc (v_int + 1); 298 if (!setting->data.v_string) 299 { 300 result = XSETTINGS_NO_MEM; 301 goto out; 302 } 303 304 memcpy (setting->data.v_string, buffer.pos, v_int); 305 setting->data.v_string[v_int] = '\0'; 306 buffer.pos += pad_len; 307 308 break; 309 case XSETTINGS_TYPE_COLOR: 310 result = fetch_ushort (&buffer, &setting->data.v_color.red); 311 if (result != XSETTINGS_SUCCESS) 312 goto out; 313 result = fetch_ushort (&buffer, &setting->data.v_color.green); 314 if (result != XSETTINGS_SUCCESS) 315 goto out; 316 result = fetch_ushort (&buffer, &setting->data.v_color.blue); 317 if (result != XSETTINGS_SUCCESS) 318 goto out; 319 result = fetch_ushort (&buffer, &setting->data.v_color.alpha); 320 if (result != XSETTINGS_SUCCESS) 321 goto out; 322 323 break; 324 default: 325 /* Quietly ignore unknown types */ 326 break; 327 } 328 329 setting->type = type; 330 331 result = xsettings_list_insert (&settings, setting); 332 if (result != XSETTINGS_SUCCESS) 333 goto out; 334 335 setting = NULL; 336 } 337 338 out: 339 340 if (result != XSETTINGS_SUCCESS) 341 { 342 switch (result) 343 { 344 case XSETTINGS_NO_MEM: 345 fprintf(stderr, "Out of memory reading XSETTINGS property\n"); 346 break; 347 case XSETTINGS_ACCESS: 348 fprintf(stderr, "Invalid XSETTINGS property (read off end)\n"); 349 break; 350 case XSETTINGS_DUPLICATE_ENTRY: 351 fprintf (stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name); 352 SDL_FALLTHROUGH; 353 case XSETTINGS_FAILED: 354 SDL_FALLTHROUGH; 355 case XSETTINGS_SUCCESS: 356 SDL_FALLTHROUGH; 357 case XSETTINGS_NO_ENTRY: 358 break; 359 } 360 361 if (setting) 362 xsettings_setting_free (setting); 363 364 xsettings_list_free (settings); 365 settings = NULL; 366 367 } 368 369 return settings; 370} 371 372static void 373read_settings (XSettingsClient *client) 374{ 375 Atom type; 376 int format; 377 unsigned long n_items; 378 unsigned long bytes_after; 379 unsigned char *data; 380 int result; 381 382 int (*old_handler) (Display *, XErrorEvent *); 383 384 XSettingsList *old_list = client->settings; 385 386 client->settings = NULL; 387 388 if (client->manager_window) 389 { 390 old_handler = X11_XSetErrorHandler (ignore_errors); 391 result = X11_XGetWindowProperty (client->display, client->manager_window, 392 client->xsettings_atom, 0, LONG_MAX, 393 False, client->xsettings_atom, 394 &type, &format, &n_items, &bytes_after, &data); 395 X11_XSetErrorHandler (old_handler); 396 397 if (result == Success && type != None) 398 { 399 if (type != client->xsettings_atom) 400 { 401 fprintf (stderr, "Invalid type for XSETTINGS property"); 402 } 403 else if (format != 8) 404 { 405 fprintf (stderr, "Invalid format for XSETTINGS property %d", format); 406 } 407 else 408 client->settings = parse_settings (data, n_items); 409 410 X11_XFree (data); 411 } 412 } 413 414 notify_changes (client, old_list); 415 xsettings_list_free (old_list); 416} 417 418static void 419add_events (Display *display, 420 Window window, 421 long mask) 422{ 423 XWindowAttributes attr; 424 425 X11_XGetWindowAttributes (display, window, &attr); 426 X11_XSelectInput (display, window, attr.your_event_mask | mask); 427} 428 429static void 430check_manager_window (XSettingsClient *client) 431{ 432 if (client->manager_window && client->watch) 433 client->watch (client->manager_window, False, 0, client->cb_data); 434 435 if (client->grab) 436 client->grab (client->display); 437 else 438 X11_XGrabServer (client->display); 439 440 client->manager_window = X11_XGetSelectionOwner (client->display, 441 client->selection_atom); 442 if (client->manager_window) 443 X11_XSelectInput (client->display, client->manager_window, 444 PropertyChangeMask | StructureNotifyMask); 445 446 if (client->ungrab) 447 client->ungrab (client->display); 448 else 449 X11_XUngrabServer (client->display); 450 451 X11_XFlush (client->display); 452 453 if (client->manager_window && client->watch) 454 { 455 if (!client->watch (client->manager_window, True, 456 PropertyChangeMask | StructureNotifyMask, 457 client->cb_data)) 458 { 459 /* Inability to watch the window probably means that it was destroyed 460 * after we ungrabbed 461 */ 462 client->manager_window = None; 463 return; 464 } 465 } 466 467 468 read_settings (client); 469} 470 471XSettingsClient * 472xsettings_client_new (Display *display, 473 int screen, 474 XSettingsNotifyFunc notify, 475 XSettingsWatchFunc watch, 476 void *cb_data) 477{ 478 return xsettings_client_new_with_grab_funcs (display, screen, notify, watch, cb_data, 479 NULL, NULL); 480} 481 482XSettingsClient * 483xsettings_client_new_with_grab_funcs (Display *display, 484 int screen, 485 XSettingsNotifyFunc notify, 486 XSettingsWatchFunc watch, 487 void *cb_data, 488 XSettingsGrabFunc grab, 489 XSettingsGrabFunc ungrab) 490{ 491 XSettingsClient *client; 492 char buffer[256]; 493 char *atom_names[3]; 494 Atom atoms[3]; 495 496 client = malloc (sizeof *client); 497 if (!client) 498 return NULL; 499 500 client->display = display; 501 client->screen = screen; 502 client->notify = notify; 503 client->watch = watch; 504 client->cb_data = cb_data; 505 client->grab = grab; 506 client->ungrab = ungrab; 507 508 client->manager_window = None; 509 client->settings = NULL; 510 511 sprintf(buffer, "_XSETTINGS_S%d", screen); 512 atom_names[0] = buffer; 513 atom_names[1] = "_XSETTINGS_SETTINGS"; 514 atom_names[2] = "MANAGER"; 515 516#ifdef HAVE_XINTERNATOMS 517 XInternAtoms (display, atom_names, 3, False, atoms); 518#else 519 atoms[0] = X11_XInternAtom (display, atom_names[0], False); 520 atoms[1] = X11_XInternAtom (display, atom_names[1], False); 521 atoms[2] = X11_XInternAtom (display, atom_names[2], False); 522#endif 523 524 client->selection_atom = atoms[0]; 525 client->xsettings_atom = atoms[1]; 526 client->manager_atom = atoms[2]; 527 528 /* Select on StructureNotify so we get MANAGER events 529 */ 530 add_events (display, RootWindow (display, screen), StructureNotifyMask); 531 532 if (client->watch) 533 client->watch (RootWindow (display, screen), True, StructureNotifyMask, 534 client->cb_data); 535 536 check_manager_window (client); 537 538 return client; 539} 540 541 542void 543xsettings_client_set_grab_func (XSettingsClient *client, 544 XSettingsGrabFunc grab) 545{ 546 client->grab = grab; 547} 548 549void 550xsettings_client_set_ungrab_func (XSettingsClient *client, 551 XSettingsGrabFunc ungrab) 552{ 553 client->ungrab = ungrab; 554} 555 556void 557xsettings_client_destroy (XSettingsClient *client) 558{ 559 if (client->watch) 560 client->watch (RootWindow (client->display, client->screen), 561 False, 0, client->cb_data); 562 if (client->manager_window && client->watch) 563 client->watch (client->manager_window, False, 0, client->cb_data); 564 565 xsettings_list_free (client->settings); 566 free (client); 567} 568 569XSettingsResult 570xsettings_client_get_setting (XSettingsClient *client, 571 const char *name, 572 XSettingsSetting **setting) 573{ 574 XSettingsSetting *search = xsettings_list_lookup (client->settings, name); 575 if (search) 576 { 577 *setting = xsettings_setting_copy (search); 578 return *setting ? XSETTINGS_SUCCESS : XSETTINGS_NO_MEM; 579 } 580 else 581 return XSETTINGS_NO_ENTRY; 582} 583 584Bool 585xsettings_client_process_event (XSettingsClient *client, 586 const XEvent *xev) 587{ 588 /* The checks here will not unlikely cause us to reread 589 * the properties from the manager window a number of 590 * times when the manager changes from A->B. But manager changes 591 * are going to be pretty rare. 592 */ 593 if (xev->xany.window == RootWindow (client->display, client->screen)) 594 { 595 if (xev->xany.type == ClientMessage && 596 xev->xclient.message_type == client->manager_atom && 597 xev->xclient.data.l[1] == client->selection_atom) 598 { 599 check_manager_window (client); 600 return True; 601 } 602 } 603 else if (xev->xany.window == client->manager_window) 604 { 605 if (xev->xany.type == DestroyNotify) 606 { 607 check_manager_window (client); 608 return False; 609 } 610 else if (xev->xany.type == PropertyNotify) 611 { 612 read_settings (client); 613 return True; 614 } 615 } 616 617 return False; 618} 619 620XSettingsSetting * 621xsettings_setting_copy (XSettingsSetting *setting) 622{ 623 XSettingsSetting *result; 624 size_t str_len; 625 626 result = malloc (sizeof *result); 627 if (!result) 628 return NULL; 629 630 str_len = strlen (setting->name); 631 result->name = malloc (str_len + 1); 632 if (!result->name) 633 goto err; 634 635 memcpy (result->name, setting->name, str_len + 1); 636 637 result->type = setting->type; 638 639 switch (setting->type) 640 { 641 case XSETTINGS_TYPE_INT: 642 result->data.v_int = setting->data.v_int; 643 break; 644 case XSETTINGS_TYPE_COLOR: 645 result->data.v_color = setting->data.v_color; 646 break; 647 case XSETTINGS_TYPE_STRING: 648 str_len = strlen (setting->data.v_string); 649 result->data.v_string = malloc (str_len + 1); 650 if (!result->data.v_string) 651 goto err; 652 653 memcpy (result->data.v_string, setting->data.v_string, str_len + 1); 654 break; 655 } 656 657 result->last_change_serial = setting->last_change_serial; 658 659 return result; 660 661 err: 662 free(result->name); // This should NOT be SDL_free() 663 free(result); // This should NOT be SDL_free() 664 665 return NULL; 666} 667 668XSettingsList * 669xsettings_list_copy (XSettingsList *list) 670{ 671 XSettingsList *new = NULL; 672 XSettingsList *old_iter = list; 673 XSettingsList *new_iter = NULL; 674 675 while (old_iter) 676 { 677 XSettingsList *new_node; 678 679 new_node = malloc (sizeof *new_node); 680 if (!new_node) 681 goto error; 682 683 new_node->setting = xsettings_setting_copy (old_iter->setting); 684 if (!new_node->setting) 685 { 686 free (new_node); 687 goto error; 688 } 689 690 if (new_iter) 691 new_iter->next = new_node; 692 else 693 { 694 new = new_node; 695 new->next = NULL; 696 } 697 698 699 new_iter = new_node; 700 701 old_iter = old_iter->next; 702 } 703 704 return new; 705 706 error: 707 xsettings_list_free (new); 708 return NULL; 709} 710 711int 712xsettings_setting_equal (XSettingsSetting *setting_a, 713 XSettingsSetting *setting_b) 714{ 715 if (setting_a->type != setting_b->type) 716 return 0; 717 718 if (strcmp (setting_a->name, setting_b->name) != 0) 719 return 0; 720 721 switch (setting_a->type) 722 { 723 case XSETTINGS_TYPE_INT: 724 return setting_a->data.v_int == setting_b->data.v_int; 725 case XSETTINGS_TYPE_COLOR: 726 return (setting_a->data.v_color.red == setting_b->data.v_color.red && 727 setting_a->data.v_color.green == setting_b->data.v_color.green && 728 setting_a->data.v_color.blue == setting_b->data.v_color.blue && 729 setting_a->data.v_color.alpha == setting_b->data.v_color.alpha); 730 case XSETTINGS_TYPE_STRING: 731 return strcmp (setting_a->data.v_string, setting_b->data.v_string) == 0; 732 } 733 734 return 0; 735} 736 737void 738xsettings_setting_free (XSettingsSetting *setting) 739{ 740 if (setting->type == XSETTINGS_TYPE_STRING) 741 free (setting->data.v_string); 742 743 free(setting->name); // This should NOT be SDL_free() 744 free(setting); // This should NOT be SDL_free() 745} 746 747void 748xsettings_list_free (XSettingsList *list) 749{ 750 while (list) 751 { 752 XSettingsList *next = list->next; 753 754 xsettings_setting_free (list->setting); 755 free (list); 756 757 list = next; 758 } 759} 760 761XSettingsResult 762xsettings_list_insert (XSettingsList **list, 763 XSettingsSetting *setting) 764{ 765 XSettingsList *node; 766 XSettingsList *iter; 767 XSettingsList *last = NULL; 768 769 node = malloc (sizeof *node); 770 if (!node) 771 return XSETTINGS_NO_MEM; 772 node->setting = setting; 773 774 iter = *list; 775 while (iter) 776 { 777 int cmp = strcmp (setting->name, iter->setting->name); 778 779 if (cmp < 0) 780 break; 781 else if (cmp == 0) 782 { 783 free (node); 784 return XSETTINGS_DUPLICATE_ENTRY; 785 } 786 787 last = iter; 788 iter = iter->next; 789 } 790 791 if (last) 792 last->next = node; 793 else 794 *list = node; 795 796 node->next = iter; 797 798 return XSETTINGS_SUCCESS; 799} 800 801XSettingsResult 802xsettings_list_delete (XSettingsList **list, 803 const char *name) 804{ 805 XSettingsList *iter; 806 XSettingsList *last = NULL; 807 808 iter = *list; 809 while (iter) 810 { 811 if (strcmp (name, iter->setting->name) == 0) 812 { 813 if (last) 814 last->next = iter->next; 815 else 816 *list = iter->next; 817 818 xsettings_setting_free (iter->setting); 819 free (iter); 820 821 return XSETTINGS_SUCCESS; 822 } 823 824 last = iter; 825 iter = iter->next; 826 } 827 828 return XSETTINGS_FAILED; 829} 830 831XSettingsSetting * 832xsettings_list_lookup (XSettingsList *list, 833 const char *name) 834{ 835 XSettingsList *iter; 836 837 iter = list; 838 while (iter) 839 { 840 if (strcmp (name, iter->setting->name) == 0) 841 return iter->setting; 842 843 iter = iter->next; 844 } 845 846 return NULL; 847} 848 849char 850xsettings_byte_order (void) 851{ 852 CARD32 myint = 0x01020304; 853 return (*(char *)&myint == 1) ? MSBFirst : LSBFirst; 854} 855 856#endif /* SDL_VIDEO_DRIVER_X11 */ 857
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.