diff options
Diffstat (limited to 'src/terminal-settings-bridge-impl.cc')
-rw-r--r-- | src/terminal-settings-bridge-impl.cc | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/src/terminal-settings-bridge-impl.cc b/src/terminal-settings-bridge-impl.cc new file mode 100644 index 0000000..da26e38 --- /dev/null +++ b/src/terminal-settings-bridge-impl.cc @@ -0,0 +1,402 @@ +/* + * Copyright © 2008, 2010, 2011, 2022 Christian Persch + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" +#define G_SETTINGS_ENABLE_BACKEND + +#include <cassert> + +#include "terminal-settings-bridge-impl.hh" + +#include "terminal-app.hh" +#include "terminal-debug.hh" +#include "terminal-libgsystem.hh" +#include "terminal-settings-utils.hh" +#include "terminal-settings-bridge-generated.h" + +#include <gio/gio.h> +#include <gio/gsettingsbackend.h> + +enum { + PROP_SETTINGS_BACKEND = 1, +}; + +struct _TerminalSettingsBridgeImpl { + TerminalSettingsBridgeSkeleton parent_instance; + + GSettingsBackend* backend; + void* tag; +}; + +struct _TerminalSettingsBridgeImplClass { + TerminalSettingsBridgeSkeletonClass parent_class; +}; + +/* helper functions */ + +template<typename T> +static inline constexpr auto +IMPL(T* that) noexcept +{ + return reinterpret_cast<TerminalSettingsBridgeImpl*>(that); +} + +static GVariantType* +type_from_string(GDBusMethodInvocation* invocation, + char const* type) noexcept +{ + if (!g_variant_type_string_is_valid(type)) { + g_dbus_method_invocation_return_error(invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "Invalid type: %s", + type); + return nullptr; + } + + return g_variant_type_new(type); +} + +static auto +value(GDBusMethodInvocation* invocation, + char const* format, + ...) noexcept +{ + va_list args; + va_start(args, format); + auto const v = g_variant_new_va(format, nullptr, &args); + va_end(args); + g_dbus_method_invocation_return_value(invocation, v); + return true; +} + +static auto +nothing(GDBusMethodInvocation* invocation) noexcept +{ + return value(invocation, "()"); +} + +static auto +novalue(GDBusMethodInvocation* invocation) noexcept +{ + g_dbus_method_invocation_return_error_literal(invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "No value"); + return true; +} + +static auto +success(GDBusMethodInvocation* invocation, + bool v = true) noexcept +{ + return value(invocation, "(b)", v); +} + +/* TerminalSettingsBridge interface implementation */ + +static gboolean +terminal_settings_bridge_impl_get_permission(TerminalSettingsBridge* object, + GDBusMethodInvocation* invocation, + char const* path) noexcept +{ + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Bridge impl ::get_permission path %s\n", + path); + + return novalue(invocation); +} + +static gboolean +terminal_settings_bridge_impl_get_writable(TerminalSettingsBridge* object, + GDBusMethodInvocation* invocation, + char const* key) noexcept +{ + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Bridge impl ::get_writable key %s\n", + key); + + auto const impl = IMPL(object); + auto const v = terminal_g_settings_backend_get_writable(impl->backend, key); + return success(invocation, v); +} + +static gboolean +terminal_settings_bridge_impl_read(TerminalSettingsBridge* object, + GDBusMethodInvocation* invocation, + char const* key, + char const* type, + gboolean default_value) noexcept +{ + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Bridge impl ::read key %s type %s default %d\n", + key, type, default_value); + + gs_free_variant_type auto vtype = type_from_string(invocation, type); + if (!vtype) + return true; + + auto const impl = IMPL(object); + gs_unref_variant auto v = terminal_g_settings_backend_read(impl->backend, + key, + vtype, + default_value); + return value(invocation, "(@ay)", terminal_g_variant_wrap(v)); +} + +static gboolean +terminal_settings_bridge_impl_read_user_value(TerminalSettingsBridge* object, + GDBusMethodInvocation* invocation, + char const* key, + char const* type) noexcept +{ + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Bridge impl ::read_user_value key %s type %s\n", + key, type); + + gs_free_variant_type auto vtype = type_from_string(invocation, type); + if (!vtype) + return true; + + auto const impl = IMPL(object); + gs_unref_variant auto v = terminal_g_settings_backend_read_user_value(impl->backend, + key, + vtype); + return value(invocation, "(@ay)", terminal_g_variant_wrap(v)); +} + +static gboolean +terminal_settings_bridge_impl_reset(TerminalSettingsBridge* object, + GDBusMethodInvocation* invocation, + char const* key) noexcept +{ + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Bridge impl ::reset key %s\n", + key); + + auto const impl = IMPL(object); + terminal_g_settings_backend_reset(impl->backend, key, impl->tag); + return nothing(invocation); +} + +static gboolean +terminal_settings_bridge_impl_subscribe(TerminalSettingsBridge* object, + GDBusMethodInvocation* invocation, + char const* name) noexcept +{ + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Bridge impl ::subscribe name %s\n", + name); + + auto const impl = IMPL(object); + terminal_g_settings_backend_subscribe(impl->backend, name); + return nothing(invocation); +} + +static gboolean +terminal_settings_bridge_impl_sync(TerminalSettingsBridge* object, + GDBusMethodInvocation* invocation) noexcept +{ + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Bridge impl ::sync\n"); + + auto const impl = IMPL(object); + terminal_g_settings_backend_sync(impl->backend); + return nothing(invocation); +} + +static gboolean +terminal_settings_bridge_impl_unsubscribe(TerminalSettingsBridge* object, + GDBusMethodInvocation* invocation, + char const* name) noexcept +{ + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Bridge impl ::unsubscribe name %s\n", + name); + + auto const impl = IMPL(object); + terminal_g_settings_backend_unsubscribe(impl->backend, name); + return nothing(invocation); +} + +static gboolean +terminal_settings_bridge_impl_write(TerminalSettingsBridge* object, + GDBusMethodInvocation* invocation, + char const* key, + GVariant* value) noexcept +{ + auto const impl = IMPL(object); + gs_unref_variant auto v = terminal_g_variant_unwrap(value); + + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Bridge impl ::write key %s value %s\n", + key, v ? g_variant_print(v, true): "(null)"); + + auto const r = terminal_g_settings_backend_write(impl->backend, + key, + v, + impl->tag); + return success(invocation, r); +} + +static gboolean +terminal_settings_bridge_impl_write_tree(TerminalSettingsBridge* object, + GDBusMethodInvocation* invocation, + char const* path_prefix, + GVariant* variant) noexcept +{ + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Bridge impl ::write_tree path-prefix %s\n", + path_prefix); + + gs_unref_variant auto tree_value = terminal_g_variant_unwrap(variant); + if (!tree_value || + !g_variant_is_of_type(tree_value, G_VARIANT_TYPE("a(smv)"))) { + _terminal_debug_print(TERMINAL_DEBUG_BRIDGE, + "Bridge impl ::write_tree got type %s expected type a(smv)\n", + tree_value ? g_variant_get_type_string(tree_value) : "(null)"); + + g_dbus_method_invocation_return_error + (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_INVALID_ARGS, + "Invalid type: got type \"%s\" expected type \"a(smv)\"", + tree_value ? g_variant_get_type_string(tree_value) : "(null)"); + return true; + } + + auto const tree = terminal_g_settings_backend_create_tree(); + + auto iter = GVariantIter{}; + g_variant_iter_init(&iter, tree_value); + + char const* key = nullptr; + GVariant* value = nullptr; + while (g_variant_iter_loop(&iter, "(&smv)", &key, &value)) { + g_tree_insert(tree, + g_strconcat(path_prefix, key, nullptr), // adopts + value ? g_variant_get_variant(value) : nullptr); + } + + auto const impl = IMPL(object); + auto const v = terminal_g_settings_backend_write_tree(impl->backend, + tree, + impl->tag); + + g_tree_unref(tree); + return success(invocation, v); +} + +static void +terminal_settings_bridge_impl_iface_init(TerminalSettingsBridgeIface* iface) noexcept +{ + iface->handle_get_permission = terminal_settings_bridge_impl_get_permission; + iface->handle_get_writable = terminal_settings_bridge_impl_get_writable; + iface->handle_read = terminal_settings_bridge_impl_read; + iface->handle_read_user_value = terminal_settings_bridge_impl_read_user_value; + iface->handle_reset = terminal_settings_bridge_impl_reset; + iface->handle_subscribe = terminal_settings_bridge_impl_subscribe; + iface->handle_sync= terminal_settings_bridge_impl_sync; + iface->handle_unsubscribe = terminal_settings_bridge_impl_unsubscribe; + iface->handle_write = terminal_settings_bridge_impl_write; + iface->handle_write_tree = terminal_settings_bridge_impl_write_tree; +} + +/* GObject class implementation */ + +G_DEFINE_TYPE_WITH_CODE(TerminalSettingsBridgeImpl, + terminal_settings_bridge_impl, + TERMINAL_TYPE_SETTINGS_BRIDGE_SKELETON, + G_IMPLEMENT_INTERFACE(TERMINAL_TYPE_SETTINGS_BRIDGE, + terminal_settings_bridge_impl_iface_init)); + +static void +terminal_settings_bridge_impl_init(TerminalSettingsBridgeImpl* impl) /* noexcept */ +{ + impl->tag = &impl->tag; +} + +static void +terminal_settings_bridge_impl_constructed(GObject* object) noexcept +{ + G_OBJECT_CLASS(terminal_settings_bridge_impl_parent_class)->constructed(object); + + auto const impl = IMPL(object); + assert(impl->backend); +} + +static void +terminal_settings_bridge_impl_finalize(GObject* object) noexcept +{ + auto const impl = IMPL(object); + g_clear_object(&impl->backend); + + G_OBJECT_CLASS(terminal_settings_bridge_impl_parent_class)->finalize(object); +} + +static void +terminal_settings_bridge_impl_set_property(GObject* object, + guint prop_id, + GValue const* value, + GParamSpec* pspec) noexcept +{ + auto const impl = IMPL(object); + + switch (prop_id) { + case PROP_SETTINGS_BACKEND: + impl->backend = G_SETTINGS_BACKEND(g_value_dup_object(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +terminal_settings_bridge_impl_class_init(TerminalSettingsBridgeImplClass* klass) /* noexcept */ +{ + auto const gobject_class = G_OBJECT_CLASS(klass); + gobject_class->constructed = terminal_settings_bridge_impl_constructed; + gobject_class->finalize = terminal_settings_bridge_impl_finalize; + gobject_class->set_property = terminal_settings_bridge_impl_set_property; + + g_object_class_install_property + (gobject_class, + PROP_SETTINGS_BACKEND, + g_param_spec_object("settings-backend", nullptr, nullptr, + G_TYPE_SETTINGS_BACKEND, + GParamFlags(G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS))); +} + +/* public API */ + +/** +* terminal_settings_bridge_impl_new: +* @backend: a #GSettingsBackend +* +* Returns: (transfer full): a new #TerminalSettingsBridgeImpl for @backend + */ +TerminalSettingsBridgeImpl* +terminal_settings_bridge_impl_new(GSettingsBackend* backend) +{ + return reinterpret_cast<TerminalSettingsBridgeImpl*> + (g_object_new (TERMINAL_TYPE_SETTINGS_BRIDGE_IMPL, + "settings-backend", backend, + nullptr)); +} |