Add portal interface to support `SDL_GetSystemTheme` in linux

This commit is contained in:
Guldoman 2023-03-09 22:26:33 +01:00 committed by Sam Lantinga
parent c304fbded6
commit ad95c93bf4
7 changed files with 237 additions and 0 deletions

View File

@ -1552,6 +1552,7 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU)
if(HAVE_DBUS_DBUS_H)
list(APPEND SOURCE_FILES "${SDL3_SOURCE_DIR}/src/core/linux/SDL_dbus.c")
list(APPEND SOURCE_FILES "${SDL3_SOURCE_DIR}/src/core/linux/SDL_system_theme.c")
endif()
if(SDL_USE_IME)

View File

@ -0,0 +1,175 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
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"
#include "SDL_dbus.h"
#include "SDL_system_theme.h"
#include "../SDL_sysvideo.h"
#include <unistd.h>
#define PORTAL_DESTINATION "org.freedesktop.portal.Desktop"
#define PORTAL_PATH "/org/freedesktop/portal/desktop"
#define PORTAL_INTERFACE "org.freedesktop.portal.Settings"
#define PORTAL_METHOD "Read"
#define SIGNAL_INTERFACE "org.freedesktop.portal.Settings"
#define SIGNAL_NAMESPACE "org.freedesktop.appearance"
#define SIGNAL_NAME "SettingChanged"
#define SIGNAL_KEY "color-scheme"
typedef struct SystemThemeData
{
SDL_DBusContext *dbus;
SDL_SystemTheme theme;
} SystemThemeData;
static SystemThemeData system_theme_data;
static SDL_bool
DBus_ExtractThemeVariant(DBusMessageIter *iter, SDL_SystemTheme *theme) {
SDL_DBusContext *dbus = system_theme_data.dbus;
Uint32 color_scheme;
DBusMessageIter variant_iter;
if (dbus->message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
return SDL_FALSE;
dbus->message_iter_recurse(iter, &variant_iter);
if (dbus->message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_UINT32)
return SDL_FALSE;
dbus->message_iter_get_basic(&variant_iter, &color_scheme);
switch (color_scheme) {
case 0:
*theme = SDL_SYSTEM_THEME_UNKNOWN;
break;
case 1:
*theme = SDL_SYSTEM_THEME_DARK;
break;
case 2:
*theme = SDL_SYSTEM_THEME_LIGHT;
break;
}
return SDL_TRUE;
}
static DBusHandlerResult
DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data) {
SDL_DBusContext *dbus = (SDL_DBusContext *)data;
if (dbus->message_is_signal(msg, SIGNAL_INTERFACE, SIGNAL_NAME)) {
DBusMessageIter signal_iter;
const char *namespace, *key;
dbus->message_iter_init(msg, &signal_iter);
/* Check if the parameters are what we expect */
if (dbus->message_iter_get_arg_type(&signal_iter) != DBUS_TYPE_STRING)
goto not_our_signal;
dbus->message_iter_get_basic(&signal_iter, &namespace);
if (SDL_strcmp(SIGNAL_NAMESPACE, namespace) != 0)
goto not_our_signal;
if (!dbus->message_iter_next(&signal_iter))
goto not_our_signal;
if (dbus->message_iter_get_arg_type(&signal_iter) != DBUS_TYPE_STRING)
goto not_our_signal;
dbus->message_iter_get_basic(&signal_iter, &key);
if (SDL_strcmp(SIGNAL_KEY, key) != 0)
goto not_our_signal;
if (!dbus->message_iter_next(&signal_iter))
goto not_our_signal;
if (!DBus_ExtractThemeVariant(&signal_iter, &system_theme_data.theme))
goto not_our_signal;
SDL_SetSystemTheme(system_theme_data.theme);
return DBUS_HANDLER_RESULT_HANDLED;
}
not_our_signal:
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
SDL_bool
SDL_SystemTheme_Init(void)
{
SDL_DBusContext *dbus = SDL_DBus_GetContext();
DBusMessage *msg;
static const char *namespace = SIGNAL_NAMESPACE;
static const char *key = SIGNAL_KEY;
system_theme_data.theme = SDL_SYSTEM_THEME_UNKNOWN;
system_theme_data.dbus = dbus;
if (dbus == NULL) {
return SDL_FALSE;
}
msg = dbus->message_new_method_call(PORTAL_DESTINATION, PORTAL_PATH, PORTAL_INTERFACE, PORTAL_METHOD);
if (msg != NULL) {
if (dbus->message_append_args(msg, DBUS_TYPE_STRING, &namespace, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID)) {
DBusMessage *reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, 300, NULL);
if (reply) {
DBusMessageIter reply_iter, variant_outer_iter;
dbus->message_iter_init(reply, &reply_iter);
/* The response has signature <<u>> */
if (dbus->message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_VARIANT)
goto incorrect_type;
dbus->message_iter_recurse(&reply_iter, &variant_outer_iter);
if (!DBus_ExtractThemeVariant(&variant_outer_iter, &system_theme_data.theme))
goto incorrect_type;
incorrect_type:
dbus->message_unref(reply);
}
}
dbus->message_unref(msg);
}
dbus->bus_add_match(dbus->session_conn,
"type='signal', interface='"SIGNAL_INTERFACE"',"
"member='"SIGNAL_NAME"', arg0='"SIGNAL_NAMESPACE"',"
"arg1='"SIGNAL_KEY"'", NULL);
dbus->connection_add_filter(dbus->session_conn,
&DBus_MessageFilter, dbus, NULL);
dbus->connection_flush(dbus->session_conn);
return SDL_TRUE;
}
SDL_SystemTheme
SDL_SystemTheme_Get(void)
{
return system_theme_data.theme;
}
void
SDL_SystemTheme_PumpEvents(void)
{
SDL_DBusContext *dbus = system_theme_data.dbus;
DBusConnection *conn;
if (dbus == NULL) return;
conn = dbus->session_conn;
dbus->connection_read_write(conn, 0);
while (dbus->connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) {
/* Do nothing, actual work happens in DBus_MessageFilter */
usleep(10);
}
}

View File

@ -0,0 +1,31 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
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.
*/
#ifndef SDL_system_theme_h_
#define SDL_system_theme_h_
#include "SDL_internal.h"
extern SDL_bool SDL_SystemTheme_Init(void);
extern SDL_SystemTheme SDL_SystemTheme_Get(void);
extern void SDL_SystemTheme_PumpEvents(void);
#endif /* SDL_system_theme_h_ */

View File

@ -26,6 +26,7 @@
#include "../../core/unix/SDL_poll.h"
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_scancode_tables_c.h"
#include "../../core/linux/SDL_system_theme.h"
#include "../SDL_sysvideo.h"
#include "SDL_waylandvideo.h"
@ -386,6 +387,10 @@ int Wayland_WaitEventTimeout(_THIS, Sint64 timeoutNS)
}
#endif
#ifdef SDL_USE_LIBDBUS
SDL_SystemTheme_PumpEvents();
#endif
/* If key repeat is active, we'll need to cap our maximum wait time to handle repeats */
if (input && keyboard_repeat_is_set(&input->keyboard_repeat)) {
const Uint64 elapsed = SDL_GetTicksNS() - input->keyboard_repeat.sdl_press_time_ns;
@ -455,6 +460,10 @@ void Wayland_PumpEvents(_THIS)
}
#endif
#ifdef SDL_USE_LIBDBUS
SDL_SystemTheme_PumpEvents();
#endif
#ifdef HAVE_LIBDECOR_H
if (d->shell.libdecor) {
libdecor_dispatch(d->shell.libdecor, 0);

View File

@ -24,6 +24,7 @@
#if SDL_VIDEO_DRIVER_WAYLAND
#include "../../events/SDL_events_c.h"
#include "../../core/linux/SDL_system_theme.h"
#include "SDL_waylandvideo.h"
#include "SDL_waylandevents_c.h"
@ -249,6 +250,11 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
device->FlashWindow = Wayland_FlashWindow;
device->HasScreenKeyboardSupport = Wayland_HasScreenKeyboardSupport;
#ifdef SDL_USE_LIBDBUS
if (SDL_SystemTheme_Init())
device->system_theme = SDL_SystemTheme_Get();
#endif
device->SetClipboardText = Wayland_SetClipboardText;
device->GetClipboardText = Wayland_GetClipboardText;
device->HasClipboardText = Wayland_HasClipboardText;

View File

@ -36,6 +36,7 @@
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_touch_c.h"
#include "../../core/linux/SDL_system_theme.h"
#include <SDL3/SDL_syswm.h>
@ -1683,6 +1684,10 @@ int X11_WaitEventTimeout(_THIS, Sint64 timeoutNS)
SDL_IME_PumpEvents();
}
#endif
#ifdef SDL_USE_LIBDBUS
SDL_SystemTheme_PumpEvents();
#endif
return 1;
}
@ -1725,6 +1730,10 @@ void X11_PumpEvents(_THIS)
}
#endif
#ifdef SDL_USE_LIBDBUS
SDL_SystemTheme_PumpEvents();
#endif
/* FIXME: Only need to do this when there are pending focus changes */
X11_HandleFocusChanges(_this);

View File

@ -26,6 +26,7 @@
#include "../SDL_sysvideo.h"
#include "../SDL_pixels_c.h"
#include "../../core/linux/SDL_system_theme.h"
#include "SDL_x11video.h"
#include "SDL_x11framebuffer.h"
@ -315,6 +316,11 @@ static SDL_VideoDevice *X11_CreateDevice(void)
device->Vulkan_CreateSurface = X11_Vulkan_CreateSurface;
#endif
#ifdef SDL_USE_LIBDBUS
if (SDL_SystemTheme_Init())
device->system_theme = SDL_SystemTheme_Get();
#endif
return device;
}