Skip to content

Commit 0929da6

Browse files
committed
Implement the settings portal
Thankfully, we don't have to implement everything - settings portal can have multiple implementations and will combine settings from all of them. Fixes #37
1 parent 7c5d4e7 commit 0929da6

11 files changed

Lines changed: 380 additions & 11 deletions
File renamed without changes.

meson.build

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,21 @@ cc = meson.get_compiler('c')
1313
m_dep = cc.find_library('m', required : false)
1414
libgeoclue_dep = dependency ('libgeoclue-2.0')
1515

16+
conf_data = configuration_data()
17+
conf_data.set('PROJECT_NAME', meson.project_name())
18+
conf_data.set('VERSION', meson.project_version())
19+
20+
config_file = configure_file(
21+
input: 'config.vala.in',
22+
output: 'config.vala',
23+
configuration: conf_data
24+
)
25+
26+
config_dep = declare_dependency(
27+
sources: config_file,
28+
include_directories: include_directories('.')
29+
)
30+
1631
prefix = get_option('prefix')
1732
datadir = join_paths(prefix, get_option('datadir'))
1833

@@ -21,5 +36,6 @@ symlink = join_paths(meson.current_source_dir (), 'meson', 'create-symlink.sh')
2136
subdir('data')
2237
subdir('po')
2338
subdir('src')
39+
subdir('settings-portal')
2440

2541
meson.add_install_script('meson/post_install.py')

meson_options.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
option('systemduserunitdir', type: 'string', value: '', description: 'custom directory for systemd user units, or \'no\' to disable')

settings-portal/Error.vala

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*-
2+
* Copyright 2021 Alexander Mikhaylenko <alexm@gnome.org>
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Library General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2 of the License, or (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Library General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Library General Public
15+
* License along with this library; if not, write to the
16+
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
17+
* Boston, MA 02110-1335 USA.
18+
*/
19+
20+
namespace XdgDesktopPortal {
21+
public enum Error {
22+
FAILED,
23+
INVALID_ARGUMENT,
24+
NOT_FOUND,
25+
EXISTS,
26+
NOT_ALLOWED,
27+
CANCELLED,
28+
WINDOW_DESTROYED
29+
}
30+
31+
const GLib.DBusErrorEntry[] ERROR_ENTRIES = {
32+
{ Error.FAILED, "org.freedesktop.portal.Error.Failed" },
33+
{ Error.INVALID_ARGUMENT, "org.freedesktop.portal.Error.InvalidArgument" },
34+
{ Error.NOT_FOUND, "org.freedesktop.portal.Error.NotFound" },
35+
{ Error.EXISTS, "org.freedesktop.portal.Error.Exists" },
36+
{ Error.NOT_ALLOWED, "org.freedesktop.portal.Error.NotAllowed" },
37+
{ Error.CANCELLED, org.freedesktop.portal.Error.Cancelled" },
38+
{ Error.WINDOW_DESTROYED, "org.freedesktop.portal.Error.WindowDestroyed" }
39+
};
40+
41+
private size_t quark = 0;
42+
43+
private GLib.Quark error_quark () {
44+
GLib.DBusError.register_error_domain (
45+
"xdg-desktop-portal-error-quark",
46+
(size_t) &quark,
47+
ERROR_ENTRIES
48+
);
49+
50+
return (GLib.Quark) quark;
51+
}
52+
53+
public GLib.Error create_error (Error code, string message) {
54+
return new GLib.Error.literal (error_quark (), code, message);
55+
}
56+
}

settings-portal/Main.vala

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*-
2+
* Copyright 2021 Alexander Mikhaylenko <alexm@gnome.org>
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Library General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2 of the License, or (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Library General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Library General Public
15+
* License along with this library; if not, write to the
16+
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
17+
* Boston, MA 02110-1335 USA.
18+
*/
19+
20+
private static bool opt_replace = false;
21+
private static bool show_version = false;
22+
23+
private static GLib.MainLoop loop;
24+
25+
private const GLib.OptionEntry[] ENTRIES = {
26+
{ "replace", 'r', 0, OptionArg.NONE, ref opt_replace, "Replace a running instance", null },
27+
{ "version", 0, 0, OptionArg.NONE, ref show_version, "Show program version.", null },
28+
{ null }
29+
};
30+
31+
private void on_bus_acquired (GLib.DBusConnection connection, string name) {
32+
try {
33+
connection.register_object ("/org/freedesktop/portal/desktop", new SettingsDaemon.Settings (connection));
34+
} catch (GLib.Error e) {
35+
critical ("Unable to register the object: %s", e.message);
36+
}
37+
}
38+
39+
public int main (string[] args) {
40+
var context = new GLib.OptionContext ("- Settings portal");
41+
context.add_main_entries (ENTRIES, null);
42+
try {
43+
context.parse (ref args);
44+
} catch (Error e) {
45+
printerr ("%s: %s", Environment.get_application_name (), e.message);
46+
printerr ("\n");
47+
printerr ("Try \"%s --help\" for more information.", GLib.Environment.get_prgname ());
48+
printerr ("\n");
49+
return 1;
50+
}
51+
52+
if (show_version) {
53+
print ("0.0 \n");
54+
return 0;
55+
}
56+
57+
loop = new GLib.MainLoop (null, false);
58+
59+
try {
60+
var session_bus = GLib.Bus.get_sync (GLib.BusType.SESSION);
61+
var owner_id = GLib.Bus.own_name (
62+
GLib.BusType.SESSION,
63+
"org.freedesktop.impl.portal.desktop.elementary.settings-daemon",
64+
GLib.BusNameOwnerFlags.ALLOW_REPLACEMENT | (opt_replace ? GLib.BusNameOwnerFlags.REPLACE : 0),
65+
on_bus_acquired,
66+
() => { debug ("org.freedesktop.impl.portal.desktop.elementary.settings acquired"); },
67+
() => { loop.quit (); }
68+
);
69+
loop.run ();
70+
GLib.Bus.unown_name (owner_id);
71+
} catch (Error e) {
72+
printerr ("No session bus: %s\n", e.message);
73+
return 2;
74+
}
75+
76+
return 0;
77+
78+
}

settings-portal/Settings.vala

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*-
2+
* Copyright 2021 Alexander Mikhaylenko <alexm@gnome.org>
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Library General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2 of the License, or (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Library General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Library General Public
15+
* License along with this library; if not, write to the
16+
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
17+
* Boston, MA 02110-1335 USA.
18+
*/
19+
20+
[DBus (name = "io.elementary.pantheon.AccountsService")]
21+
private interface Pantheon.AccountsService : Object {
22+
public abstract int prefers_color_scheme { owned get; set; }
23+
}
24+
25+
[DBus (name = "org.freedesktop.Accounts")]
26+
interface FDO.Accounts : Object {
27+
public abstract string find_user_by_name (string username) throws GLib.Error;
28+
}
29+
30+
private class AccountsServiceMonitor : GLib.Object {
31+
private FDO.Accounts? accounts_service = null;
32+
private Pantheon.AccountsService? pantheon_act = null;
33+
private string user_path;
34+
35+
public int32 color_scheme { get; set; }
36+
37+
construct {
38+
setup_user_path ();
39+
setup_prefers_color_scheme ();
40+
}
41+
42+
private void setup_user_path () {
43+
try {
44+
accounts_service = GLib.Bus.get_proxy_sync (
45+
GLib.BusType.SYSTEM,
46+
"org.freedesktop.Accounts",
47+
"/org/freedesktop/Accounts"
48+
);
49+
50+
user_path = accounts_service.find_user_by_name (GLib.Environment.get_user_name ());
51+
} catch (Error e) {
52+
critical (e.message);
53+
}
54+
}
55+
56+
private void setup_prefers_color_scheme () {
57+
try {
58+
pantheon_act = GLib.Bus.get_proxy_sync (
59+
GLib.BusType.SYSTEM,
60+
"org.freedesktop.Accounts",
61+
user_path,
62+
GLib.DBusProxyFlags.GET_INVALIDATED_PROPERTIES
63+
);
64+
65+
color_scheme = pantheon_act.prefers_color_scheme;
66+
67+
((GLib.DBusProxy) pantheon_act).g_properties_changed.connect ((changed, invalid) => {
68+
var value = changed.lookup_value ("PrefersColorScheme", new VariantType ("i"));
69+
if (value != null) {
70+
color_scheme = value.get_int32 ();
71+
}
72+
});
73+
} catch (Error e) {
74+
critical (e.message);
75+
}
76+
}
77+
}
78+
79+
[DBus (name = "org.freedesktop.impl.portal.Settings")]
80+
public class SettingsDaemon.Settings : GLib.Object {
81+
private GLib.DBusConnection connection;
82+
83+
public uint32 version {
84+
get { return 1; }
85+
}
86+
87+
public signal void setting_changed (string namespace, string key, GLib.Variant value);
88+
89+
private AccountsServiceMonitor monitor;
90+
91+
public Settings (GLib.DBusConnection connection) {
92+
this.connection = connection;
93+
}
94+
95+
construct {
96+
monitor = new AccountsServiceMonitor ();
97+
monitor.notify["color-scheme"].connect (() => {
98+
var color_scheme = new Variant.uint32 (monitor.color_scheme);
99+
setting_changed ("org.freedesktop.appearance", "color-scheme", get_color_scheme ());
100+
});
101+
}
102+
103+
private bool namespace_matches (string namespace, string[] patterns) {
104+
foreach (var pattern in patterns) {
105+
if (pattern[0] == '\0') {
106+
return true;
107+
}
108+
109+
if (pattern == namespace) {
110+
return true;
111+
}
112+
113+
int pattern_len = pattern.length;
114+
if (pattern[pattern_len - 1] == '*' && Posix.strncmp (namespace, pattern, pattern_len - 1) == 0) {
115+
return true;
116+
}
117+
}
118+
119+
return patterns.length == 0;
120+
}
121+
122+
private GLib.Variant get_color_scheme () {
123+
return new GLib.Variant.uint32 (monitor.color_scheme);
124+
}
125+
126+
public async void read_all (string[] namespaces, out GLib.Variant value) throws GLib.DBusError, GLib.IOError {
127+
var builder = new GLib.VariantBuilder (new GLib.VariantType ("(a{sa{sv}})"));
128+
129+
builder.open (new GLib.VariantType ("a{sa{sv}}"));
130+
131+
if (namespace_matches ("org.freedesktop.appearance", namespaces)) {
132+
var dict = new VariantDict ();
133+
134+
dict.insert_value ("color-scheme", get_color_scheme ());
135+
136+
builder.add ("{sa{sv}}", "org.freedesktop.appearance", dict.end ());
137+
}
138+
139+
builder.close ();
140+
141+
value = builder.end ();
142+
}
143+
144+
public async void read (string namespace, string key, out GLib.Variant value) throws GLib.DBusError, GLib.Error {
145+
debug ("Read %s %s", namespace, key);
146+
147+
if (namespace == "org.freedesktop.appearance" && key == "color-scheme") {
148+
value = get_color_scheme ();
149+
return;
150+
}
151+
152+
debug ("Attempted to read unknown namespace/key pair: %s %s", namespace, key);
153+
154+
throw XdgDesktopPortal.create_error (NOT_FOUND, "Requested setting not found");
155+
}
156+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[portal]
2+
DBusName=org.freedesktop.impl.portal.desktop.elementary.settings-daemon
3+
Interfaces=org.freedesktop.impl.portal.Settings
4+
UseIn=pantheon
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[Unit]
2+
Description=Portal service (Pantheon Settings Daemon)
3+
4+
[Service]
5+
Type=dbus
6+
BusName=org.freedesktop.impl.portal.desktop.elementary.settings-daemon
7+
ExecStart=@libexecdir@/io.elementary.settings-daemon.xdg-desktop-portal

settings-portal/meson.build

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
libexec_dir = join_paths(get_option('prefix'), get_option ('libexecdir'))
2+
3+
portal_sources = files(
4+
'Error.vala',
5+
'Main.vala',
6+
'Settings.vala',
7+
)
8+
9+
valac = meson.get_compiler('vala')
10+
posix_dep = valac.find_library('posix')
11+
12+
executable(
13+
'io.elementary.settings-daemon.xdg-desktop-portal',
14+
portal_sources,
15+
dependencies: [
16+
config_dep,
17+
glib_dep,
18+
gio_dep,
19+
posix_dep,
20+
],
21+
install: true,
22+
install_dir: libexec_dir,
23+
)
24+
25+
portal_conf_data = configuration_data()
26+
portal_conf_data.set('libexecdir', libexec_dir)
27+
28+
systemd_systemduserunitdir = get_option('systemduserunitdir')
29+
if systemd_systemduserunitdir != 'no'
30+
31+
if systemd_systemduserunitdir == ''
32+
systemd_dep = dependency('systemd', version: '>= 206', required: false)
33+
assert(systemd_dep.found(), 'systemd required but not found, please provide a valid systemd user unit dir or disable it')
34+
systemd_systemduserunitdir = systemd_dep.get_pkgconfig_variable('systemduserunitdir', define_variable: ['prefix', get_option('prefix')])
35+
endif
36+
37+
configure_file(
38+
input: 'io.elementary.settings-daemon.xdg-desktop-portal.service.in',
39+
output: '@BASENAME@',
40+
configuration: portal_conf_data,
41+
install: true,
42+
install_dir: systemd_systemduserunitdir
43+
)
44+
endif
45+
46+
install_data(
47+
'io.elementary.settings-daemon.portal',
48+
install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'xdg-desktop-portal', 'portals')
49+
)
50+
51+
configure_file(
52+
input: 'org.freedesktop.impl.portal.desktop.elementary.settings-daemon.service.in',
53+
output: '@BASENAME@',
54+
configuration: portal_conf_data,
55+
install: true,
56+
install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'dbus-1', 'services')
57+
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[D-BUS Service]
2+
Name=org.freedesktop.impl.portal.desktop.elementary.settings-daemon
3+
Exec=@libexecdir@/io.elementary.settings-daemon.xdg-desktop-portal
4+
SystemdService=io.elementary.settings-daemon.xdg-desktop-portal.service

0 commit comments

Comments
 (0)