diff --git a/src/config.vala.in b/config.vala.in similarity index 100% rename from src/config.vala.in rename to config.vala.in diff --git a/meson.build b/meson.build index 78535439..dff5b370 100644 --- a/meson.build +++ b/meson.build @@ -13,6 +13,21 @@ cc = meson.get_compiler('c') m_dep = cc.find_library('m', required : false) libgeoclue_dep = dependency ('libgeoclue-2.0') +conf_data = configuration_data() +conf_data.set('PROJECT_NAME', meson.project_name()) +conf_data.set('VERSION', meson.project_version()) + +config_file = configure_file( + input: 'config.vala.in', + output: 'config.vala', + configuration: conf_data +) + +config_dep = declare_dependency( + sources: config_file, + include_directories: include_directories('.') +) + prefix = get_option('prefix') datadir = join_paths(prefix, get_option('datadir')) @@ -21,5 +36,6 @@ symlink = join_paths(meson.current_source_dir (), 'meson', 'create-symlink.sh') subdir('data') subdir('po') subdir('src') +subdir('settings-portal') meson.add_install_script('meson/post_install.py') diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 00000000..d1195c4b --- /dev/null +++ b/meson_options.txt @@ -0,0 +1 @@ +option('systemduserunitdir', type: 'string', value: '', description: 'custom directory for systemd user units, or \'no\' to disable') diff --git a/settings-portal/Main.vala b/settings-portal/Main.vala new file mode 100644 index 00000000..cd440768 --- /dev/null +++ b/settings-portal/Main.vala @@ -0,0 +1,80 @@ +/*- + * Copyright 2021 elementary, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1335 USA. + * + * Authored by: Corentin Noël + */ + +private static bool opt_replace = false; +private static bool show_version = false; + +private static GLib.MainLoop loop; + +private const GLib.OptionEntry[] ENTRIES = { + { "replace", 'r', 0, OptionArg.NONE, ref opt_replace, "Replace a running instance", null }, + { "version", 0, 0, OptionArg.NONE, ref show_version, "Show program version.", null }, + { null } +}; + +private void on_bus_acquired (GLib.DBusConnection connection, string name) { + try { + connection.register_object ("/org/freedesktop/portal/desktop", new SettingsDaemon.Settings ()); + } catch (GLib.Error e) { + critical ("Unable to register the object: %s", e.message); + } +} + +public int main (string[] args) { + var context = new GLib.OptionContext ("- Settings portal"); + context.add_main_entries (ENTRIES, null); + try { + context.parse (ref args); + } catch (Error e) { + printerr ("%s: %s", Environment.get_application_name (), e.message); + printerr ("\n"); + printerr ("Try \"%s --help\" for more information.", GLib.Environment.get_prgname ()); + printerr ("\n"); + return 1; + } + + if (show_version) { + print ("%s \n", Build.VERSION); + return 0; + } + + loop = new GLib.MainLoop (null, false); + + try { + var session_bus = GLib.Bus.get_sync (GLib.BusType.SESSION); + var owner_id = GLib.Bus.own_name ( + GLib.BusType.SESSION, + "org.freedesktop.impl.portal.desktop.elementary.settings-daemon", + GLib.BusNameOwnerFlags.ALLOW_REPLACEMENT | (opt_replace ? GLib.BusNameOwnerFlags.REPLACE : 0), + on_bus_acquired, + () => { debug ("org.freedesktop.impl.portal.desktop.elementary.settings acquired"); }, + () => { loop.quit (); } + ); + loop.run (); + GLib.Bus.unown_name (owner_id); + } catch (Error e) { + printerr ("No session bus: %s\n", e.message); + return 2; + } + + return 0; + +} diff --git a/settings-portal/Settings.vala b/settings-portal/Settings.vala new file mode 100644 index 00000000..8a5cbf69 --- /dev/null +++ b/settings-portal/Settings.vala @@ -0,0 +1,155 @@ +/*- + * Copyright 2021 elementary, Inc. (https://elementary.io) + * Copyright 2021 Alexander Mikhaylenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1335 USA. + */ + +[DBus (name = "org.freedesktop.portal.Error")] +public errordomain PortalError { + FAILED, + INVALID_ARGUMENT, + NOT_FOUND, + EXISTS, + NOT_ALLOWED, + CANCELLED, + WINDOW_DESTROYED +} + +[DBus (name = "io.elementary.pantheon.AccountsService")] +private interface Pantheon.AccountsService : Object { + public abstract int prefers_color_scheme { owned get; set; } +} + +[DBus (name = "org.freedesktop.Accounts")] +interface FDO.Accounts : Object { + public abstract string find_user_by_name (string username) throws GLib.Error; +} + +/* Copied from Granite.Settings */ +private class AccountsServiceMonitor : GLib.Object { + private FDO.Accounts? accounts_service = null; + private Pantheon.AccountsService? pantheon_act = null; + private string user_path; + + public int32 color_scheme { get; set; } + + construct { + setup_user_path (); + setup_prefers_color_scheme (); + } + + private void setup_user_path () { + try { + accounts_service = GLib.Bus.get_proxy_sync ( + GLib.BusType.SYSTEM, + "org.freedesktop.Accounts", + "/org/freedesktop/Accounts" + ); + + user_path = accounts_service.find_user_by_name (GLib.Environment.get_user_name ()); + } catch (Error e) { + critical (e.message); + } + } + + private void setup_prefers_color_scheme () { + try { + pantheon_act = GLib.Bus.get_proxy_sync ( + GLib.BusType.SYSTEM, + "org.freedesktop.Accounts", + user_path, + GLib.DBusProxyFlags.GET_INVALIDATED_PROPERTIES + ); + + color_scheme = pantheon_act.prefers_color_scheme; + + ((GLib.DBusProxy) pantheon_act).g_properties_changed.connect ((changed, invalid) => { + var value = changed.lookup_value ("PrefersColorScheme", new VariantType ("i")); + if (value != null) { + color_scheme = value.get_int32 (); + } + }); + } catch (Error e) { + critical (e.message); + } + } +} + +[DBus (name = "org.freedesktop.impl.portal.Settings")] +public class SettingsDaemon.Settings : GLib.Object { + public uint32 version { + get { return 1; } + } + + public signal void setting_changed (string namespace, string key, GLib.Variant value); + + private AccountsServiceMonitor monitor; + + construct { + monitor = new AccountsServiceMonitor (); + monitor.notify["color-scheme"].connect (() => { + setting_changed ("org.freedesktop.appearance", "color-scheme", get_color_scheme ()); + }); + } + + private bool namespace_matches (string namespace, string[] patterns) { + foreach (var pattern in patterns) { + if (pattern[0] == '\0') { + return true; + } + + if (pattern == namespace) { + return true; + } + + int pattern_len = pattern.length; + if (pattern[pattern_len - 1] == '*' && namespace.has_prefix (pattern.slice (0, pattern_len - 1))) { + return true; + } + } + + return patterns.length == 0; + } + + private GLib.Variant get_color_scheme () { + return new GLib.Variant.uint32 (monitor.color_scheme); + } + + public async GLib.HashTable> read_all (string[] namespaces) throws GLib.DBusError, GLib.IOError { + var ret = new GLib.HashTable> (str_hash, str_equal); + + if (namespace_matches ("org.freedesktop.appearance", namespaces)) { + var dict = new HashTable (str_hash, str_equal); + + dict.insert ("color-scheme", get_color_scheme ()); + + ret.insert ("org.freedesktop.appearance", dict); + } + + return ret; + } + + public async GLib.Variant read (string namespace, string key) throws GLib.DBusError, GLib.Error { + if (namespace == "org.freedesktop.appearance" && key == "color-scheme") { + return get_color_scheme (); + } + + debug ("Attempted to read unknown namespace/key pair: %s %s", namespace, key); + + throw new PortalError.NOT_FOUND ("Requested setting not found"); + } +} diff --git a/settings-portal/io.elementary.settings-daemon.portal b/settings-portal/io.elementary.settings-daemon.portal new file mode 100644 index 00000000..6da980a5 --- /dev/null +++ b/settings-portal/io.elementary.settings-daemon.portal @@ -0,0 +1,4 @@ +[portal] +DBusName=org.freedesktop.impl.portal.desktop.elementary.settings-daemon +Interfaces=org.freedesktop.impl.portal.Settings +UseIn=pantheon diff --git a/settings-portal/io.elementary.settings-daemon.xdg-desktop-portal.service.in b/settings-portal/io.elementary.settings-daemon.xdg-desktop-portal.service.in new file mode 100644 index 00000000..7ff6f514 --- /dev/null +++ b/settings-portal/io.elementary.settings-daemon.xdg-desktop-portal.service.in @@ -0,0 +1,7 @@ +[Unit] +Description=Portal service (Pantheon Settings Daemon) + +[Service] +Type=dbus +BusName=org.freedesktop.impl.portal.desktop.elementary.settings-daemon +ExecStart=@libexecdir@/io.elementary.settings-daemon.xdg-desktop-portal diff --git a/settings-portal/meson.build b/settings-portal/meson.build new file mode 100644 index 00000000..93ea855c --- /dev/null +++ b/settings-portal/meson.build @@ -0,0 +1,52 @@ +libexec_dir = join_paths(get_option('prefix'), get_option ('libexecdir')) + +portal_sources = files( + 'Main.vala', + 'Settings.vala', +) + +executable( + 'io.elementary.settings-daemon.xdg-desktop-portal', + portal_sources, + dependencies: [ + config_dep, + glib_dep, + gio_dep, + ], + install: true, + install_dir: libexec_dir, +) + +portal_conf_data = configuration_data() +portal_conf_data.set('libexecdir', libexec_dir) + +systemd_systemduserunitdir = get_option('systemduserunitdir') +if systemd_systemduserunitdir != 'no' + + if systemd_systemduserunitdir == '' + systemd_dep = dependency('systemd', version: '>= 206', required: false) + assert(systemd_dep.found(), 'systemd required but not found, please provide a valid systemd user unit dir or disable it') + systemd_systemduserunitdir = systemd_dep.get_pkgconfig_variable('systemduserunitdir', define_variable: ['prefix', get_option('prefix')]) + endif + + configure_file( + input: 'io.elementary.settings-daemon.xdg-desktop-portal.service.in', + output: '@BASENAME@', + configuration: portal_conf_data, + install: true, + install_dir: systemd_systemduserunitdir + ) +endif + +install_data( + 'io.elementary.settings-daemon.portal', + install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'xdg-desktop-portal', 'portals') +) + +configure_file( + input: 'org.freedesktop.impl.portal.desktop.elementary.settings-daemon.service.in', + output: '@BASENAME@', + configuration: portal_conf_data, + install: true, + install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'dbus-1', 'services') +) diff --git a/settings-portal/org.freedesktop.impl.portal.desktop.elementary.settings-daemon.service.in b/settings-portal/org.freedesktop.impl.portal.desktop.elementary.settings-daemon.service.in new file mode 100644 index 00000000..35c978ad --- /dev/null +++ b/settings-portal/org.freedesktop.impl.portal.desktop.elementary.settings-daemon.service.in @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.freedesktop.impl.portal.desktop.elementary.settings-daemon +Exec=@libexecdir@/io.elementary.settings-daemon.xdg-desktop-portal +SystemdService=io.elementary.settings-daemon.xdg-desktop-portal.service diff --git a/src/meson.build b/src/meson.build index 8ced24ee..6ccdebe6 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,13 +1,3 @@ -conf_data = configuration_data() -conf_data.set('PROJECT_NAME', meson.project_name()) -conf_data.set('VERSION', meson.project_version()) - -config_file = configure_file( - input: 'config.vala.in', - output: 'config.vala', - configuration: conf_data -) - sources = files( 'AccountsService.vala', 'Application.vala', @@ -21,8 +11,8 @@ sources = files( executable( meson.project_name(), sources, - config_file, dependencies: [ + config_dep, gio_dep, glib_dep, granite_dep,