Add XSETTINGS support to x11 driver

Import the XSettingsClient implementation to handle the settings
selection.

Currently, we only care about the Gdk/WindowScalingFactor value used by
the windowing system to notify us of display-wide changes in the scaling
factor.
This commit is contained in:
Emmanuele Bassi 2023-11-30 16:36:22 +00:00 committed by Sam Lantinga
parent d6da494c1c
commit 61dafb3b2f
8 changed files with 1174 additions and 1 deletions

View file

@ -104,7 +104,10 @@ function(get_clang_tidy_ignored_files OUTVAR)
"hid.m"
"hidraw.cpp"
"hidusb.cpp"
"hidapi.h")
"hidapi.h"
# XSETTINGS
"xsettings-client.c"
"xsettings-client.h")
foreach(SOURCE_FILE ${3RD_PARTY_SOURCES})
list(APPEND IGNORED_LIST "{\"name\":\"${SOURCE_FILE}\",\"lines\":[[1,1]]}")

View file

@ -33,6 +33,7 @@
#include "SDL_x11touch.h"
#include "SDL_x11xinput2.h"
#include "SDL_x11xfixes.h"
#include "SDL_x11settings.h"
#include "../SDL_clipboard_c.h"
#include "../../core/unix/SDL_poll.h"
#include "../../events/SDL_events_c.h"
@ -779,6 +780,16 @@ static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xeven
}
}
static void X11_HandleSettingsEvent(SDL_VideoDevice *_this, const XEvent *xevent)
{
SDL_VideoData *videodata = _this->driverdata;
SDL_assert(videodata->xsettings_window != None);
SDL_assert(xevent->xany.window == videodata->xsettings_window);
X11_HandleXsettings(_this, xevent);
}
static Bool isMapNotify(Display *display, XEvent *ev, XPointer arg)
{
XUnmapEvent *unmap;
@ -1103,6 +1114,12 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
return;
}
if ((videodata->xsettings_window != None) &&
(videodata->xsettings_window == xevent->xany.window)) {
X11_HandleSettingsEvent(_this, xevent);
return;
}
data = X11_FindWindow(_this, xevent->xany.window);
if (!data) {

View file

@ -0,0 +1,97 @@
/*
Simple DirectMedia Layer
Copyright 2024 Igalia S.L.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_X11)
#include "SDL_x11video.h"
#include "SDL_x11settings.h"
#define SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR "Gdk/WindowScalingFactor"
static void X11_XsettingsNotify(const char *name, XSettingsAction action, XSettingsSetting *setting, void *data)
{
SDL_VideoDevice *_this = data;
float scale_factor = 1.0;
int i;
if (SDL_strcmp(name, SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR) != 0) {
return;
}
if (setting->type != XSETTINGS_TYPE_INT) {
return;
}
switch (action) {
case XSETTINGS_ACTION_NEW:
SDL_FALLTHROUGH;
case XSETTINGS_ACTION_CHANGED:
scale_factor = setting->data.v_int;
break;
case XSETTINGS_ACTION_DELETED:
scale_factor = 1.0;
break;
}
if (_this) {
for (i = 0; i < _this->num_displays; ++i) {
SDL_SetDisplayContentScale(_this->displays[i], scale_factor);
}
}
}
void X11_InitXsettings(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->driverdata;
SDLX11_SettingsData *xsettings_data = &data->xsettings_data;
xsettings_data->xsettings = xsettings_client_new(data->display,
DefaultScreen(data->display), X11_XsettingsNotify, NULL, _this);
}
void X11_QuitXsettings(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->driverdata;
SDLX11_SettingsData *xsettings_data = &data->xsettings_data;
if (xsettings_data->xsettings) {
xsettings_client_destroy(xsettings_data->xsettings);
xsettings_data->xsettings = NULL;
}
}
void X11_HandleXsettings(SDL_VideoDevice *_this, const XEvent *xevent)
{
SDL_VideoData *data = _this->driverdata;
SDLX11_SettingsData *xsettings_data = &data->xsettings_data;
if (xsettings_data->xsettings) {
if (!xsettings_client_process_event(xsettings_data->xsettings, xevent)) {
xsettings_client_destroy(xsettings_data->xsettings);
xsettings_data->xsettings = NULL;
}
}
}
#endif /* SDL_VIDEO_DRIVER_X11 */

View file

@ -0,0 +1,38 @@
/*
Simple DirectMedia Layer
Copyright 2024 Igalia S.L.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11settings_h_
#define SDL_x11settings_h_
#include <X11/Xlib.h>
#include "xsettings-client.h"
typedef struct X11_SettingsData {
XSettingsClient *xsettings;
} SDLX11_SettingsData;
extern void X11_InitXsettings(SDL_VideoDevice *_this);
extern void X11_QuitXsettings(SDL_VideoDevice *_this);
extern void X11_HandleXsettings(SDL_VideoDevice *_this, const XEvent *xevent);
#endif /* SDL_x11settings_h_ */

View file

@ -443,6 +443,8 @@ int X11_VideoInit(SDL_VideoDevice *_this)
X11_InitXfixes(_this);
#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
X11_InitXsettings(_this);
#ifndef X_HAVE_UTF8_STRING
#warning X server does not support UTF8_STRING, a feature introduced in 2000! This is likely to become a hard error in a future libSDL3.
#endif
@ -469,6 +471,10 @@ void X11_VideoQuit(SDL_VideoDevice *_this)
X11_XDestroyWindow(data->display, data->clipboard_window);
}
if (data->xsettings_window) {
X11_XDestroyWindow(data->display, data->xsettings_window);
}
#ifdef X_HAVE_UTF8_STRING
if (data->im) {
X11_XCloseIM(data->im);
@ -480,6 +486,7 @@ void X11_VideoQuit(SDL_VideoDevice *_this)
X11_QuitMouse(_this);
X11_QuitTouch(_this);
X11_QuitClipboard(_this);
X11_QuitXsettings(_this);
}
SDL_bool X11_UseDirectColorVisuals(void)

View file

@ -36,6 +36,7 @@
#include "SDL_x11modes.h"
#include "SDL_x11mouse.h"
#include "SDL_x11opengl.h"
#include "SDL_x11settings.h"
#include "SDL_x11window.h"
#include "SDL_x11vulkan.h"
@ -58,6 +59,8 @@ struct SDL_VideoData
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
SDL_Window *active_cursor_confined_window;
#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
Window xsettings_window;
SDLX11_SettingsData xsettings_data;
/* This is true for ICCCM2.0-compliant window managers */
SDL_bool net_wm;

View file

@ -0,0 +1,855 @@
/*
* Copyright © 2001, 2007 Red Hat, Inc.
* Copyright 2024 Igalia S.L.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Red Hat not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. Red Hat makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: Owen Taylor, Red Hat, Inc.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_X11
#include "SDL_x11video.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xsettings-client.h"
struct _XSettingsClient
{
Display *display;
int screen;
XSettingsNotifyFunc notify;
XSettingsWatchFunc watch;
void *cb_data;
XSettingsGrabFunc grab;
XSettingsGrabFunc ungrab;
Window manager_window;
Atom manager_atom;
Atom selection_atom;
Atom xsettings_atom;
XSettingsList *settings;
};
static void
notify_changes (XSettingsClient *client,
XSettingsList *old_list)
{
XSettingsList *old_iter = old_list;
XSettingsList *new_iter = client->settings;
if (!client->notify)
return;
while (old_iter || new_iter)
{
int cmp;
if (old_iter && new_iter)
cmp = strcmp (old_iter->setting->name, new_iter->setting->name);
else if (old_iter)
cmp = -1;
else
cmp = 1;
if (cmp < 0)
{
client->notify (old_iter->setting->name,
XSETTINGS_ACTION_DELETED,
NULL,
client->cb_data);
}
else if (cmp == 0)
{
if (!xsettings_setting_equal (old_iter->setting,
new_iter->setting))
client->notify (old_iter->setting->name,
XSETTINGS_ACTION_CHANGED,
new_iter->setting,
client->cb_data);
}
else
{
client->notify (new_iter->setting->name,
XSETTINGS_ACTION_NEW,
new_iter->setting,
client->cb_data);
}
if (old_iter)
old_iter = old_iter->next;
if (new_iter)
new_iter = new_iter->next;
}
}
static int
ignore_errors (Display *display, XErrorEvent *event)
{
return True;
}
static char local_byte_order = '\0';
#define BYTES_LEFT(buffer) ((buffer)->data + (buffer)->len - (buffer)->pos)
static XSettingsResult
fetch_card16 (XSettingsBuffer *buffer,
CARD16 *result)
{
CARD16 x;
if (BYTES_LEFT (buffer) < 2)
return XSETTINGS_ACCESS;
x = *(CARD16 *)buffer->pos;
buffer->pos += 2;
if (buffer->byte_order == local_byte_order)
*result = x;
else
*result = (x << 8) | (x >> 8);
return XSETTINGS_SUCCESS;
}
static XSettingsResult
fetch_ushort (XSettingsBuffer *buffer,
unsigned short *result)
{
CARD16 x;
XSettingsResult r;
r = fetch_card16 (buffer, &x);
if (r == XSETTINGS_SUCCESS)
*result = x;
return r;
}
static XSettingsResult
fetch_card32 (XSettingsBuffer *buffer,
CARD32 *result)
{
CARD32 x;
if (BYTES_LEFT (buffer) < 4)
return XSETTINGS_ACCESS;
x = *(CARD32 *)buffer->pos;
buffer->pos += 4;
if (buffer->byte_order == local_byte_order)
*result = x;
else
*result = (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
return XSETTINGS_SUCCESS;
}
static XSettingsResult
fetch_card8 (XSettingsBuffer *buffer,
CARD8 *result)
{
if (BYTES_LEFT (buffer) < 1)
return XSETTINGS_ACCESS;
*result = *(CARD8 *)buffer->pos;
buffer->pos += 1;
return XSETTINGS_SUCCESS;
}
#define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1)))
static XSettingsList *
parse_settings (unsigned char *data,
size_t len)
{
XSettingsBuffer buffer;
XSettingsResult result = XSETTINGS_SUCCESS;
XSettingsList *settings = NULL;
CARD32 serial;
CARD32 n_entries;
CARD32 i;
XSettingsSetting *setting = NULL;
char buffer_byte_order = '\0';
local_byte_order = xsettings_byte_order ();
buffer.pos = buffer.data = data;
buffer.len = len;
buffer.byte_order = '\0';
result = fetch_card8 (&buffer, (unsigned char *) &buffer_byte_order);
if (buffer_byte_order != MSBFirst &&
buffer_byte_order != LSBFirst)
{
fprintf (stderr, "Invalid byte order in XSETTINGS property\n");
result = XSETTINGS_FAILED;
goto out;
}
buffer.byte_order = buffer_byte_order;
buffer.pos += 3;
result = fetch_card32 (&buffer, &serial);
if (result != XSETTINGS_SUCCESS)
goto out;
result = fetch_card32 (&buffer, &n_entries);
if (result != XSETTINGS_SUCCESS)
goto out;
for (i = 0; i < n_entries; i++)
{
CARD8 type;
CARD16 name_len;
CARD32 v_int;
size_t pad_len;
result = fetch_card8 (&buffer, &type);
if (result != XSETTINGS_SUCCESS)
goto out;
buffer.pos += 1;
result = fetch_card16 (&buffer, &name_len);
if (result != XSETTINGS_SUCCESS)
goto out;
pad_len = XSETTINGS_PAD(name_len, 4);
if (BYTES_LEFT (&buffer) < pad_len)
{
result = XSETTINGS_ACCESS;
goto out;
}
setting = malloc (sizeof *setting);
if (!setting)
{
result = XSETTINGS_NO_MEM;
goto out;
}
setting->type = XSETTINGS_TYPE_INT; /* No allocated memory */
setting->name = malloc (name_len + 1);
if (!setting->name)
{
result = XSETTINGS_NO_MEM;
goto out;
}
memcpy (setting->name, buffer.pos, name_len);
setting->name[name_len] = '\0';
buffer.pos += pad_len;
result = fetch_card32 (&buffer, &v_int);
if (result != XSETTINGS_SUCCESS)
goto out;
setting->last_change_serial = v_int;
switch (type)
{
case XSETTINGS_TYPE_INT:
result = fetch_card32 (&buffer, &v_int);
if (result != XSETTINGS_SUCCESS)
goto out;
setting->data.v_int = (INT32)v_int;
break;
case XSETTINGS_TYPE_STRING:
result = fetch_card32 (&buffer, &v_int);
if (result != XSETTINGS_SUCCESS)
goto out;
pad_len = XSETTINGS_PAD (v_int, 4);
if (v_int + 1 == 0 || /* Guard against wrap-around */
BYTES_LEFT (&buffer) < pad_len)
{
result = XSETTINGS_ACCESS;
goto out;
}
setting->data.v_string = malloc (v_int + 1);
if (!setting->data.v_string)
{
result = XSETTINGS_NO_MEM;
goto out;
}
memcpy (setting->data.v_string, buffer.pos, v_int);
setting->data.v_string[v_int] = '\0';
buffer.pos += pad_len;
break;
case XSETTINGS_TYPE_COLOR:
result = fetch_ushort (&buffer, &setting->data.v_color.red);
if (result != XSETTINGS_SUCCESS)
goto out;
result = fetch_ushort (&buffer, &setting->data.v_color.green);
if (result != XSETTINGS_SUCCESS)
goto out;
result = fetch_ushort (&buffer, &setting->data.v_color.blue);
if (result != XSETTINGS_SUCCESS)
goto out;
result = fetch_ushort (&buffer, &setting->data.v_color.alpha);
if (result != XSETTINGS_SUCCESS)
goto out;
break;
default:
/* Quietly ignore unknown types */
break;
}
setting->type = type;
result = xsettings_list_insert (&settings, setting);
if (result != XSETTINGS_SUCCESS)
goto out;
setting = NULL;
}
out:
if (result != XSETTINGS_SUCCESS)
{
switch (result)
{
case XSETTINGS_NO_MEM:
fprintf(stderr, "Out of memory reading XSETTINGS property\n");
break;
case XSETTINGS_ACCESS:
fprintf(stderr, "Invalid XSETTINGS property (read off end)\n");
break;
case XSETTINGS_DUPLICATE_ENTRY:
fprintf (stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name);
SDL_FALLTHROUGH;
case XSETTINGS_FAILED:
SDL_FALLTHROUGH;
case XSETTINGS_SUCCESS:
SDL_FALLTHROUGH;
case XSETTINGS_NO_ENTRY:
break;
}
if (setting)
xsettings_setting_free (setting);
xsettings_list_free (settings);
settings = NULL;
}
return settings;
}
static void
read_settings (XSettingsClient *client)
{
Atom type;
int format;
unsigned long n_items;
unsigned long bytes_after;
unsigned char *data;
int result;
int (*old_handler) (Display *, XErrorEvent *);
XSettingsList *old_list = client->settings;
client->settings = NULL;
if (client->manager_window)
{
old_handler = X11_XSetErrorHandler (ignore_errors);
result = X11_XGetWindowProperty (client->display, client->manager_window,
client->xsettings_atom, 0, LONG_MAX,
False, client->xsettings_atom,
&type, &format, &n_items, &bytes_after, &data);
X11_XSetErrorHandler (old_handler);
if (result == Success && type != None)
{
if (type != client->xsettings_atom)
{
fprintf (stderr, "Invalid type for XSETTINGS property");
}
else if (format != 8)
{
fprintf (stderr, "Invalid format for XSETTINGS property %d", format);
}
else
client->settings = parse_settings (data, n_items);
X11_XFree (data);
}
}
notify_changes (client, old_list);
xsettings_list_free (old_list);
}
static void
add_events (Display *display,
Window window,
long mask)
{
XWindowAttributes attr;
X11_XGetWindowAttributes (display, window, &attr);
X11_XSelectInput (display, window, attr.your_event_mask | mask);
}
static void
check_manager_window (XSettingsClient *client)
{
if (client->manager_window && client->watch)
client->watch (client->manager_window, False, 0, client->cb_data);
if (client->grab)
client->grab (client->display);
else
X11_XGrabServer (client->display);
client->manager_window = X11_XGetSelectionOwner (client->display,
client->selection_atom);
if (client->manager_window)
X11_XSelectInput (client->display, client->manager_window,
PropertyChangeMask | StructureNotifyMask);
if (client->ungrab)
client->ungrab (client->display);
else
X11_XUngrabServer (client->display);
X11_XFlush (client->display);
if (client->manager_window && client->watch)
{
if (!client->watch (client->manager_window, True,
PropertyChangeMask | StructureNotifyMask,
client->cb_data))
{
/* Inability to watch the window probably means that it was destroyed
* after we ungrabbed
*/
client->manager_window = None;
return;
}
}
read_settings (client);
}
XSettingsClient *
xsettings_client_new (Display *display,
int screen,
XSettingsNotifyFunc notify,
XSettingsWatchFunc watch,
void *cb_data)
{
return xsettings_client_new_with_grab_funcs (display, screen, notify, watch, cb_data,
NULL, NULL);
}
XSettingsClient *
xsettings_client_new_with_grab_funcs (Display *display,
int screen,
XSettingsNotifyFunc notify,
XSettingsWatchFunc watch,
void *cb_data,
XSettingsGrabFunc grab,
XSettingsGrabFunc ungrab)
{
XSettingsClient *client;
char buffer[256];
char *atom_names[3];
Atom atoms[3];
client = malloc (sizeof *client);
if (!client)
return NULL;
client->display = display;
client->screen = screen;
client->notify = notify;
client->watch = watch;
client->cb_data = cb_data;
client->grab = grab;
client->ungrab = ungrab;
client->manager_window = None;
client->settings = NULL;
sprintf(buffer, "_XSETTINGS_S%d", screen);
atom_names[0] = buffer;
atom_names[1] = "_XSETTINGS_SETTINGS";
atom_names[2] = "MANAGER";
#ifdef HAVE_XINTERNATOMS
XInternAtoms (display, atom_names, 3, False, atoms);
#else
atoms[0] = X11_XInternAtom (display, atom_names[0], False);
atoms[1] = X11_XInternAtom (display, atom_names[1], False);
atoms[2] = X11_XInternAtom (display, atom_names[2], False);
#endif
client->selection_atom = atoms[0];
client->xsettings_atom = atoms[1];
client->manager_atom = atoms[2];
/* Select on StructureNotify so we get MANAGER events
*/
add_events (display, RootWindow (display, screen), StructureNotifyMask);
if (client->watch)
client->watch (RootWindow (display, screen), True, StructureNotifyMask,
client->cb_data);
check_manager_window (client);
return client;
}
void
xsettings_client_set_grab_func (XSettingsClient *client,
XSettingsGrabFunc grab)
{
client->grab = grab;
}
void
xsettings_client_set_ungrab_func (XSettingsClient *client,
XSettingsGrabFunc ungrab)
{
client->ungrab = ungrab;
}
void
xsettings_client_destroy (XSettingsClient *client)
{
if (client->watch)
client->watch (RootWindow (client->display, client->screen),
False, 0, client->cb_data);
if (client->manager_window && client->watch)
client->watch (client->manager_window, False, 0, client->cb_data);
xsettings_list_free (client->settings);
free (client);
}
XSettingsResult
xsettings_client_get_setting (XSettingsClient *client,
const char *name,
XSettingsSetting **setting)
{
XSettingsSetting *search = xsettings_list_lookup (client->settings, name);
if (search)
{
*setting = xsettings_setting_copy (search);
return *setting ? XSETTINGS_SUCCESS : XSETTINGS_NO_MEM;
}
else
return XSETTINGS_NO_ENTRY;
}
Bool
xsettings_client_process_event (XSettingsClient *client,
const XEvent *xev)
{
/* The checks here will not unlikely cause us to reread
* the properties from the manager window a number of
* times when the manager changes from A->B. But manager changes
* are going to be pretty rare.
*/
if (xev->xany.window == RootWindow (client->display, client->screen))
{
if (xev->xany.type == ClientMessage &&
xev->xclient.message_type == client->manager_atom &&
xev->xclient.data.l[1] == client->selection_atom)
{
check_manager_window (client);
return True;
}
}
else if (xev->xany.window == client->manager_window)
{
if (xev->xany.type == DestroyNotify)
{
check_manager_window (client);
return False;
}
else if (xev->xany.type == PropertyNotify)
{
read_settings (client);
return True;
}
}
return False;
}
XSettingsSetting *
xsettings_setting_copy (XSettingsSetting *setting)
{
XSettingsSetting *result;
size_t str_len;
result = malloc (sizeof *result);
if (!result)
return NULL;
str_len = strlen (setting->name);
result->name = malloc (str_len + 1);
if (!result->name)
goto err;
memcpy (result->name, setting->name, str_len + 1);
result->type = setting->type;
switch (setting->type)
{
case XSETTINGS_TYPE_INT:
result->data.v_int = setting->data.v_int;
break;
case XSETTINGS_TYPE_COLOR:
result->data.v_color = setting->data.v_color;
break;
case XSETTINGS_TYPE_STRING:
str_len = strlen (setting->data.v_string);
result->data.v_string = malloc (str_len + 1);
if (!result->data.v_string)
goto err;
memcpy (result->data.v_string, setting->data.v_string, str_len + 1);
break;
}
result->last_change_serial = setting->last_change_serial;
return result;
err:
if (result->name)
free (result->name);
free (result);
return NULL;
}
XSettingsList *
xsettings_list_copy (XSettingsList *list)
{
XSettingsList *new = NULL;
XSettingsList *old_iter = list;
XSettingsList *new_iter = NULL;
while (old_iter)
{
XSettingsList *new_node;
new_node = malloc (sizeof *new_node);
if (!new_node)
goto error;
new_node->setting = xsettings_setting_copy (old_iter->setting);
if (!new_node->setting)
{
free (new_node);
goto error;
}
if (new_iter)
new_iter->next = new_node;
else
new = new_node;
new_iter = new_node;
old_iter = old_iter->next;
}
return new;
error:
xsettings_list_free (new);
return NULL;
}
int
xsettings_setting_equal (XSettingsSetting *setting_a,
XSettingsSetting *setting_b)
{
if (setting_a->type != setting_b->type)
return 0;
if (strcmp (setting_a->name, setting_b->name) != 0)
return 0;
switch (setting_a->type)
{
case XSETTINGS_TYPE_INT:
return setting_a->data.v_int == setting_b->data.v_int;
case XSETTINGS_TYPE_COLOR:
return (setting_a->data.v_color.red == setting_b->data.v_color.red &&
setting_a->data.v_color.green == setting_b->data.v_color.green &&
setting_a->data.v_color.blue == setting_b->data.v_color.blue &&
setting_a->data.v_color.alpha == setting_b->data.v_color.alpha);
case XSETTINGS_TYPE_STRING:
return strcmp (setting_a->data.v_string, setting_b->data.v_string) == 0;
}
return 0;
}
void
xsettings_setting_free (XSettingsSetting *setting)
{
if (setting->type == XSETTINGS_TYPE_STRING)
free (setting->data.v_string);
if (setting->name)
free (setting->name);
free (setting);
}
void
xsettings_list_free (XSettingsList *list)
{
while (list)
{
XSettingsList *next = list->next;
xsettings_setting_free (list->setting);
free (list);
list = next;
}
}
XSettingsResult
xsettings_list_insert (XSettingsList **list,
XSettingsSetting *setting)
{
XSettingsList *node;
XSettingsList *iter;
XSettingsList *last = NULL;
node = malloc (sizeof *node);
if (!node)
return XSETTINGS_NO_MEM;
node->setting = setting;
iter = *list;
while (iter)
{
int cmp = strcmp (setting->name, iter->setting->name);
if (cmp < 0)
break;
else if (cmp == 0)
{
free (node);
return XSETTINGS_DUPLICATE_ENTRY;
}
last = iter;
iter = iter->next;
}
if (last)
last->next = node;
else
*list = node;
node->next = iter;
return XSETTINGS_SUCCESS;
}
XSettingsResult
xsettings_list_delete (XSettingsList **list,
const char *name)
{
XSettingsList *iter;
XSettingsList *last = NULL;
iter = *list;
while (iter)
{
if (strcmp (name, iter->setting->name) == 0)
{
if (last)
last->next = iter->next;
else
*list = iter->next;
xsettings_setting_free (iter->setting);
free (iter);
return XSETTINGS_SUCCESS;
}
last = iter;
iter = iter->next;
}
return XSETTINGS_FAILED;
}
XSettingsSetting *
xsettings_list_lookup (XSettingsList *list,
const char *name)
{
XSettingsList *iter;
iter = list;
while (iter)
{
if (strcmp (name, iter->setting->name) == 0)
return iter->setting;
iter = iter->next;
}
return NULL;
}
char
xsettings_byte_order (void)
{
CARD32 myint = 0x01020304;
return (*(char *)&myint == 1) ? MSBFirst : LSBFirst;
}
#endif /* SDL_VIDEO_DRIVER_X11 */

View file

@ -0,0 +1,153 @@
/*
* Copyright © 2001, 2007 Red Hat, Inc.
* Copyright 2024 Igalia S.L.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Red Hat not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. Red Hat makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: Owen Taylor, Red Hat, Inc.
*/
#ifndef XSETTINGS_CLIENT_H
#define XSETTINGS_CLIENT_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct _XSettingsBuffer XSettingsBuffer;
typedef struct _XSettingsColor XSettingsColor;
typedef struct _XSettingsList XSettingsList;
typedef struct _XSettingsSetting XSettingsSetting;
/* Types of settings possible. Enum values correspond to
* protocol values.
*/
typedef enum
{
XSETTINGS_TYPE_INT = 0,
XSETTINGS_TYPE_STRING = 1,
XSETTINGS_TYPE_COLOR = 2
} XSettingsType;
typedef enum
{
XSETTINGS_SUCCESS,
XSETTINGS_NO_MEM,
XSETTINGS_ACCESS,
XSETTINGS_FAILED,
XSETTINGS_NO_ENTRY,
XSETTINGS_DUPLICATE_ENTRY
} XSettingsResult;
struct _XSettingsBuffer
{
char byte_order;
size_t len;
unsigned char *data;
unsigned char *pos;
};
struct _XSettingsColor
{
unsigned short red, green, blue, alpha;
};
struct _XSettingsList
{
XSettingsSetting *setting;
XSettingsList *next;
};
struct _XSettingsSetting
{
char *name;
XSettingsType type;
union {
int v_int;
char *v_string;
XSettingsColor v_color;
} data;
unsigned long last_change_serial;
};
XSettingsSetting *xsettings_setting_copy (XSettingsSetting *setting);
void xsettings_setting_free (XSettingsSetting *setting);
int xsettings_setting_equal (XSettingsSetting *setting_a,
XSettingsSetting *setting_b);
void xsettings_list_free (XSettingsList *list);
XSettingsList *xsettings_list_copy (XSettingsList *list);
XSettingsResult xsettings_list_insert (XSettingsList **list,
XSettingsSetting *setting);
XSettingsSetting *xsettings_list_lookup (XSettingsList *list,
const char *name);
XSettingsResult xsettings_list_delete (XSettingsList **list,
const char *name);
char xsettings_byte_order (void);
#define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1)))
typedef struct _XSettingsClient XSettingsClient;
typedef enum
{
XSETTINGS_ACTION_NEW,
XSETTINGS_ACTION_CHANGED,
XSETTINGS_ACTION_DELETED
} XSettingsAction;
typedef void (*XSettingsNotifyFunc) (const char *name,
XSettingsAction action,
XSettingsSetting *setting,
void *cb_data);
typedef Bool (*XSettingsWatchFunc) (Window window,
Bool is_start,
long mask,
void *cb_data);
typedef void (*XSettingsGrabFunc) (Display *display);
XSettingsClient *xsettings_client_new (Display *display,
int screen,
XSettingsNotifyFunc notify,
XSettingsWatchFunc watch,
void *cb_data);
XSettingsClient *xsettings_client_new_with_grab_funcs (Display *display,
int screen,
XSettingsNotifyFunc notify,
XSettingsWatchFunc watch,
void *cb_data,
XSettingsGrabFunc grab,
XSettingsGrabFunc ungrab);
void xsettings_client_set_grab_func (XSettingsClient *client,
XSettingsGrabFunc grab);
void xsettings_client_set_ungrab_func (XSettingsClient *client,
XSettingsGrabFunc ungrab);
void xsettings_client_destroy (XSettingsClient *client);
Bool xsettings_client_process_event (XSettingsClient *client,
const XEvent *xev);
XSettingsResult xsettings_client_get_setting (XSettingsClient *client,
const char *name,
XSettingsSetting **setting);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* XSETTINGS_CLIENT_H */