/* * 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 . */ #include "config.h" #define G_SETTINGS_ENABLE_BACKEND #include #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 #include enum { PROP_SETTINGS_BACKEND = 1, }; struct _TerminalSettingsBridgeImpl { TerminalSettingsBridgeSkeleton parent_instance; GSettingsBackend* backend; void* tag; }; struct _TerminalSettingsBridgeImplClass { TerminalSettingsBridgeSkeletonClass parent_class; }; /* helper functions */ template static inline constexpr auto IMPL(T* that) noexcept { return reinterpret_cast(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 (g_object_new (TERMINAL_TYPE_SETTINGS_BRIDGE_IMPL, "settings-backend", backend, nullptr)); }