Skip to content

Commit b364954

Browse files
authored
Implement the settings portal (#38)
1 parent 55917e6 commit b364954

10 files changed

Lines changed: 320 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/Main.vala

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*-
2+
* Copyright 2021 elementary, Inc. <https://elementary.io>
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+
* Authored by: Corentin Noël <corentin@elementary.io>
20+
*/
21+
22+
private static bool opt_replace = false;
23+
private static bool show_version = false;
24+
25+
private static GLib.MainLoop loop;
26+
27+
private const GLib.OptionEntry[] ENTRIES = {
28+
{ "replace", 'r', 0, OptionArg.NONE, ref opt_replace, "Replace a running instance", null },
29+
{ "version", 0, 0, OptionArg.NONE, ref show_version, "Show program version.", null },
30+
{ null }
31+
};
32+
33+
private void on_bus_acquired (GLib.DBusConnection connection, string name) {
34+
try {
35+
connection.register_object ("/org/freedesktop/portal/desktop", new SettingsDaemon.Settings ());
36+
} catch (GLib.Error e) {
37+
critical ("Unable to register the object: %s", e.message);
38+
}
39+
}
40+
41+
public int main (string[] args) {
42+
var context = new GLib.OptionContext ("- Settings portal");
43+
context.add_main_entries (ENTRIES, null);
44+
try {
45+
context.parse (ref args);
46+
} catch (Error e) {
47+
printerr ("%s: %s", Environment.get_application_name (), e.message);
48+
printerr ("\n");
49+
printerr ("Try \"%s --help\" for more information.", GLib.Environment.get_prgname ());
50+
printerr ("\n");
51+
return 1;
52+
}
53+
54+
if (show_version) {
55+
print ("%s \n", Build.VERSION);
56+
return 0;
57+
}
58+
59+
loop = new GLib.MainLoop (null, false);
60+
61+
try {
62+
var session_bus = GLib.Bus.get_sync (GLib.BusType.SESSION);
63+
var owner_id = GLib.Bus.own_name (
64+
GLib.BusType.SESSION,
65+
"org.freedesktop.impl.portal.desktop.elementary.settings-daemon",
66+
GLib.BusNameOwnerFlags.ALLOW_REPLACEMENT | (opt_replace ? GLib.BusNameOwnerFlags.REPLACE : 0),
67+
on_bus_acquired,
68+
() => { debug ("org.freedesktop.impl.portal.desktop.elementary.settings acquired"); },
69+
() => { loop.quit (); }
70+
);
71+
loop.run ();
72+
GLib.Bus.unown_name (owner_id);
73+
} catch (Error e) {
74+
printerr ("No session bus: %s\n", e.message);
75+
return 2;
76+
}
77+
78+
return 0;
79+
80+
}

settings-portal/Settings.vala

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*-
2+
* Copyright 2021 elementary, Inc. (https://elementary.io)
3+
* Copyright 2021 Alexander Mikhaylenko <alexm@gnome.org>
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Library General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 2 of the License, or (at your option) any later version.
9+
*
10+
* This library is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Library General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Library General Public
16+
* License along with this library; if not, write to the
17+
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor
18+
* Boston, MA 02110-1335 USA.
19+
*/
20+
21+
[DBus (name = "org.freedesktop.portal.Error")]
22+
public errordomain PortalError {
23+
FAILED,
24+
INVALID_ARGUMENT,
25+
NOT_FOUND,
26+
EXISTS,
27+
NOT_ALLOWED,
28+
CANCELLED,
29+
WINDOW_DESTROYED
30+
}
31+
32+
[DBus (name = "io.elementary.pantheon.AccountsService")]
33+
private interface Pantheon.AccountsService : Object {
34+
public abstract int prefers_color_scheme { owned get; set; }
35+
}
36+
37+
[DBus (name = "org.freedesktop.Accounts")]
38+
interface FDO.Accounts : Object {
39+
public abstract string find_user_by_name (string username) throws GLib.Error;
40+
}
41+
42+
/* Copied from Granite.Settings */
43+
private class AccountsServiceMonitor : GLib.Object {
44+
private FDO.Accounts? accounts_service = null;
45+
private Pantheon.AccountsService? pantheon_act = null;
46+
private string user_path;
47+
48+
public int32 color_scheme { get; set; }
49+
50+
construct {
51+
setup_user_path ();
52+
setup_prefers_color_scheme ();
53+
}
54+
55+
private void setup_user_path () {
56+
try {
57+
accounts_service = GLib.Bus.get_proxy_sync (
58+
GLib.BusType.SYSTEM,
59+
"org.freedesktop.Accounts",
60+
"/org/freedesktop/Accounts"
61+
);
62+
63+
user_path = accounts_service.find_user_by_name (GLib.Environment.get_user_name ());
64+
} catch (Error e) {
65+
critical (e.message);
66+
}
67+
}
68+
69+
private void setup_prefers_color_scheme () {
70+
try {
71+
pantheon_act = GLib.Bus.get_proxy_sync (
72+
GLib.BusType.SYSTEM,
73+
"org.freedesktop.Accounts",
74+
user_path,
75+
GLib.DBusProxyFlags.GET_INVALIDATED_PROPERTIES
76+
);
77+
78+
color_scheme = pantheon_act.prefers_color_scheme;
79+
80+
((GLib.DBusProxy) pantheon_act).g_properties_changed.connect ((changed, invalid) => {
81+
var value = changed.lookup_value ("PrefersColorScheme", new VariantType ("i"));
82+
if (value != null) {
83+
color_scheme = value.get_int32 ();
84+
}
85+
});
86+
} catch (Error e) {
87+
critical (e.message);
88+
}
89+
}
90+
}
91+
92+
[DBus (name = "org.freedesktop.impl.portal.Settings")]
93+
public class SettingsDaemon.Settings : GLib.Object {
94+
public uint32 version {
95+
get { return 1; }
96+
}
97+
98+
public signal void setting_changed (string namespace, string key, GLib.Variant value);
99+
100+
private AccountsServiceMonitor monitor;
101+
102+
construct {
103+
monitor = new AccountsServiceMonitor ();
104+
monitor.notify["color-scheme"].connect (() => {
105+
setting_changed ("org.freedesktop.appearance", "color-scheme", get_color_scheme ());
106+
});
107+
}
108+
109+
private bool namespace_matches (string namespace, string[] patterns) {
110+
foreach (var pattern in patterns) {
111+
if (pattern[0] == '\0') {
112+
return true;
113+
}
114+
115+
if (pattern == namespace) {
116+
return true;
117+
}
118+
119+
int pattern_len = pattern.length;
120+
if (pattern[pattern_len - 1] == '*' && namespace.has_prefix (pattern.slice (0, pattern_len - 1))) {
121+
return true;
122+
}
123+
}
124+
125+
return patterns.length == 0;
126+
}
127+
128+
private GLib.Variant get_color_scheme () {
129+
return new GLib.Variant.uint32 (monitor.color_scheme);
130+
}
131+
132+
public async GLib.HashTable<string, GLib.HashTable<string, GLib.Variant>> read_all (string[] namespaces) throws GLib.DBusError, GLib.IOError {
133+
var ret = new GLib.HashTable<string, GLib.HashTable<string, GLib.Variant>> (str_hash, str_equal);
134+
135+
if (namespace_matches ("org.freedesktop.appearance", namespaces)) {
136+
var dict = new HashTable<string, Variant> (str_hash, str_equal);
137+
138+
dict.insert ("color-scheme", get_color_scheme ());
139+
140+
ret.insert ("org.freedesktop.appearance", dict);
141+
}
142+
143+
return ret;
144+
}
145+
146+
public async GLib.Variant read (string namespace, string key) throws GLib.DBusError, GLib.Error {
147+
if (namespace == "org.freedesktop.appearance" && key == "color-scheme") {
148+
return get_color_scheme ();
149+
}
150+
151+
debug ("Attempted to read unknown namespace/key pair: %s %s", namespace, key);
152+
153+
throw new PortalError.NOT_FOUND ("Requested setting not found");
154+
}
155+
}
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: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
libexec_dir = join_paths(get_option('prefix'), get_option ('libexecdir'))
2+
3+
portal_sources = files(
4+
'Main.vala',
5+
'Settings.vala',
6+
)
7+
8+
executable(
9+
'io.elementary.settings-daemon.xdg-desktop-portal',
10+
portal_sources,
11+
dependencies: [
12+
config_dep,
13+
glib_dep,
14+
gio_dep,
15+
],
16+
install: true,
17+
install_dir: libexec_dir,
18+
)
19+
20+
portal_conf_data = configuration_data()
21+
portal_conf_data.set('libexecdir', libexec_dir)
22+
23+
systemd_systemduserunitdir = get_option('systemduserunitdir')
24+
if systemd_systemduserunitdir != 'no'
25+
26+
if systemd_systemduserunitdir == ''
27+
systemd_dep = dependency('systemd', version: '>= 206', required: false)
28+
assert(systemd_dep.found(), 'systemd required but not found, please provide a valid systemd user unit dir or disable it')
29+
systemd_systemduserunitdir = systemd_dep.get_pkgconfig_variable('systemduserunitdir', define_variable: ['prefix', get_option('prefix')])
30+
endif
31+
32+
configure_file(
33+
input: 'io.elementary.settings-daemon.xdg-desktop-portal.service.in',
34+
output: '@BASENAME@',
35+
configuration: portal_conf_data,
36+
install: true,
37+
install_dir: systemd_systemduserunitdir
38+
)
39+
endif
40+
41+
install_data(
42+
'io.elementary.settings-daemon.portal',
43+
install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'xdg-desktop-portal', 'portals')
44+
)
45+
46+
configure_file(
47+
input: 'org.freedesktop.impl.portal.desktop.elementary.settings-daemon.service.in',
48+
output: '@BASENAME@',
49+
configuration: portal_conf_data,
50+
install: true,
51+
install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'dbus-1', 'services')
52+
)
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

src/meson.build

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
conf_data = configuration_data()
2-
conf_data.set('PROJECT_NAME', meson.project_name())
3-
conf_data.set('VERSION', meson.project_version())
4-
5-
config_file = configure_file(
6-
input: 'config.vala.in',
7-
output: 'config.vala',
8-
configuration: conf_data
9-
)
10-
111
sources = files(
122
'AccountsService.vala',
133
'Application.vala',
@@ -21,8 +11,8 @@ sources = files(
2111
executable(
2212
meson.project_name(),
2313
sources,
24-
config_file,
2514
dependencies: [
15+
config_dep,
2616
gio_dep,
2717
glib_dep,
2818
granite_dep,

0 commit comments

Comments
 (0)