[xdg-desktop-portal-gnome/gbsneto/more-portals: 5/5] Implement the Account portal
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [xdg-desktop-portal-gnome/gbsneto/more-portals: 5/5] Implement the Account portal
- Date: Tue, 14 Dec 2021 13:40:22 +0000 (UTC)
commit 72baec236010840e935b8f2bf52592bea874801c
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Tue Dec 14 10:38:27 2021 -0300
Implement the Account portal
Closes https://gitlab.gnome.org/GNOME/xdg-desktop-portal-gnome/-/issues/7
data/gnome.portal | 2 +-
data/org.freedesktop.Accounts.User.xml | 815 +++++++++++++++++++++++++++++
data/org.freedesktop.Accounts.xml | 228 ++++++++
src/account.c | 260 +++++++++
src/account.h | 26 +
src/accountdialog.c | 252 +++++++++
src/accountdialog.h | 17 +
src/accountdialog.ui | 100 ++++
src/meson.build | 5 +
src/xdg-desktop-portal-gnome.c | 7 +
src/xdg-desktop-portal-gnome.gresource.xml | 1 +
11 files changed, 1712 insertions(+), 1 deletion(-)
---
diff --git a/data/gnome.portal b/data/gnome.portal
index d47d251..38a6f85 100644
--- a/data/gnome.portal
+++ b/data/gnome.portal
@@ -1,4 +1,4 @@
[portal]
DBusName=org.freedesktop.impl.portal.desktop.gnome
-Interfaces=org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.RemoteDesktop;org.freedesktop.impl.portal.Lockdown;org.freedesktop.impl.portal.Background;org.freedesktop.impl.portal.Settings;org.freedesktop.impl.portal.Wallpaper;org.freedesktop.impl.portal.FileChooser;org.freedesktop.impl.portal.Print;
+Interfaces=org.freedesktop.impl.portal.Account;org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.RemoteDesktop;org.freedesktop.impl.portal.Lockdown;org.freedesktop.impl.portal.Background;org.freedesktop.impl.portal.Settings;org.freedesktop.impl.portal.Wallpaper;org.freedesktop.impl.portal.FileChooser;org.freedesktop.impl.portal.Print;
UseIn=gnome
diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml
new file mode 100644
index 0000000..a6a8b5e
--- /dev/null
+++ b/data/org.freedesktop.Accounts.User.xml
@@ -0,0 +1,815 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" >
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+ <interface name="org.freedesktop.Accounts.User">
+
+ <method name="SetUserName">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="name" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new username.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users username. Note that it is usually not allowed
+ to have multiple users with the same username.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the username of any user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetRealName">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="name" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new name, typically in the form "Firstname Lastname".
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users real name.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own name</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the name of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetEmail">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="email" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new email address.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users email address.
+ </doc:para>
+ <doc:para>
+ Note that setting an email address in the AccountsService is
+ not the same as configuring a mail client. Mail clients might
+ default to email address that is configured here, though.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own email address</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the email address of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetLanguage">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="language" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new language, as a locale specification like "de_DE.UTF-8".
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users language.
+ </doc:para>
+ <doc:para>
+ The expectation is that display managers will start the
+ users session with this locale.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own language</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the language of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetXSession">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="user_set_x_session"/>
+ <arg name="x_session" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new xsession to start (e.g. "gnome")
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users x session.
+ </doc:para>
+ <doc:para>
+ The expectation is that display managers will log the user in to this
+ specified session, if available.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own language</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the language of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetLocation">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="location" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new location as a freeform string.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users location.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own location</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the location of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetHomeDirectory">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="homedir" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new homedir as an absolute path.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users home directory.
+ </doc:para>
+ <doc:para>
+ Note that changing the users home directory moves all the content
+ from the old location to the new one, and is potentially an
+ expensive operation.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the home directory of a user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetShell">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="shell" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The new user shell.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users shell.
+ </doc:para>
+ <doc:para>
+ Note that setting the shell to a non-allowed program may
+ prevent the user from logging in.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the shell of a user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetIconFile">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="filename" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The absolute filename of a png file to use as the users icon.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users icon.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own icon</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the icon of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetLocked">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="locked" direction="in" type="b">
+ <doc:doc>
+ <doc:summary>
+ Whether to lock or unlock the users account.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Locks or unlocks a users account.
+ </doc:para>
+ <doc:para>
+ Locking an account prevents the user from logging in.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To lock or unlock user accounts</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetAccountType">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="accountType" direction="in" type="i">
+ <doc:doc>
+ <doc:summary>
+ The new account type, encoded as an integer:
+ <doc:list>
+ <doc:item>
+ <doc:term>0</doc:term>
+ <doc:definition>Standard user</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>1</doc:term>
+ <doc:definition>Administrator</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Changes the users account type.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change an account type</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetPasswordMode">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="mode" direction="in" type="i">
+ <doc:doc>
+ <doc:summary>
+ The new password mode, encoded as an integer:
+ <doc:list>
+ <doc:item>
+ <doc:term>0</doc:term>
+ <doc:definition>Regular password</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>1</doc:term>
+ <doc:definition>Password must be set at next login</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>2</doc:term>
+ <doc:definition>No password</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Changes the users password mode.
+ </doc:para>
+ <doc:para>
+ Note that changing the password mode has the side-effect of
+ unlocking the account.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change a users password mode</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetPassword">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="password" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The crypted password.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <arg name="hint" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The password hint.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets a new password for this user.
+ </doc:para>
+ <doc:para>
+ Note that setting a password has the side-effect of
+ unlocking the account.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the password of a user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetPasswordHint">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="hint" direction="in" type="s">
+ <doc:doc>
+ <doc:summary>
+ The password hint.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the users password hint.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change his own language</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the language of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="SetAutomaticLogin">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="enabled" direction="in" type="b">
+ <doc:doc>
+ <doc:summary>
+ Whether to enable automatic login for this user.
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Enables or disables automatic login for a user.
+ </doc:para>
+ <doc:para>
+ Note that usually only one user can have automatic login
+ enabled, so turning it on for a user will disable it for
+ the previously configured autologin user.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.set-login-option</doc:term>
+ <doc:definition>To change the login screen configuration</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <property name="Uid" type="t" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The uid of the user.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="UserName" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The username of the user.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="RealName" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users real name.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="AccountType" type="i" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users account type, encoded as an integer:
+ <doc:list>
+ <doc:item>
+ <doc:term>0</doc:term>
+ <doc:definition>Standard user</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>1</doc:term>
+ <doc:definition>Administrator</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="HomeDirectory" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users home directory.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="Shell" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users shell.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="Email" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The email address.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="Language" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users language, as a locale specification like "de_DE.UTF-8".
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="XSession" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users x session.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="Location" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The users location.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="LoginFrequency" type="t" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ How often the user has logged in.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="LoginTime" type="x" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The last login time.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="LoginHistory" type="a(xxa{sv})" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The login history for this user.
+ Each entry in the array represents a login session. The first two
+ members are the login time and logout time, as timestamps (seconds since the epoch). If the
session is still running, the logout time
+ is 0.
+ </doc:para>
+ <doc:para>
+ The a{sv} member is a dictionary containing additional information
+ about the session. Possible members include 'type' (with values like ':0', 'tty0', 'pts/0' etc).
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="IconFile" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The filename of a png file containing the users icon.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="Locked" type="b" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Whether the users account is locked.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="PasswordMode" type="i" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The password mode for the user account, encoded as an integer:
+ <doc:list>
+ <doc:item>
+ <doc:term>0</doc:term>
+ <doc:definition>Regular password</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>1</doc:term>
+ <doc:definition>Password must be set at next login</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>2</doc:term>
+ <doc:definition>No password</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="PasswordHint" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The password hint for the user.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="AutomaticLogin" type="b" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Whether automatic login is enabled for the user.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="SystemAccount" type="b" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Whether this is a 'system' account, like 'root' or 'nobody'.
+ System accounts should normally not appear in lists of
+ users, and ListCachedUsers will not include such accounts.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <property name="LocalAccount" type="b" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Whether the user is a local account or not.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ <signal name="Changed">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Emitted when the user is changed.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+
+ </interface>
+</node>
diff --git a/data/org.freedesktop.Accounts.xml b/data/org.freedesktop.Accounts.xml
new file mode 100644
index 0000000..692540a
--- /dev/null
+++ b/data/org.freedesktop.Accounts.xml
@@ -0,0 +1,228 @@
+<!DOCTYPE node PUBLIC
+"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" >
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+ <interface name="org.freedesktop.Accounts">
+
+ <!-- ************************************************************ -->
+
+ <method name="ListCachedUsers">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="users" direction="out" type="ao">
+ <doc:doc><doc:summary>Object paths of cached users</doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Lists users which have logged into the system locally before.
+ This is not meant to return an exhaustive list of all users.
+ It is possible for <doc:ref type="method" to="Accounts.FindUserByName">FindUserByName()</doc:ref>
+ to return a user that's not on the list.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </method>
+
+ <method name="FindUserById">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="id" direction="in" type="x">
+ <doc:doc><doc:summary>The uid to look up</doc:summary></doc:doc>
+ </arg>
+ <arg name="user" direction="out" type="o">
+ <doc:doc><doc:summary>Object path of user</doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Finds a user by uid.
+ </doc:para>
+ </doc:description>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if no user with the given uid
exists</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="FindUserByName">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="name" direction="in" type="s">
+ <doc:doc><doc:summary>The username to look up</doc:summary></doc:doc>
+ </arg>
+ <arg name="user" direction="out" type="o">
+ <doc:doc><doc:summary>Object path of user</doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Finds a user by its username.
+ </doc:para>
+ </doc:description>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if no user with the given username
exists</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="CreateUser">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="name" direction="in" type="s">
+ <doc:doc><doc:summary>The username for the new user</doc:summary></doc:doc>
+ </arg>
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="fullname" direction="in" type="s">
+ <doc:doc><doc:summary>The real name for the new user</doc:summary></doc:doc>
+ </arg>
+ <arg name="user" direction="out" type="o">
+ <doc:doc><doc:summary>Object path of the new user</doc:summary></doc:doc>
+ </arg>
+ <arg name="accountType" direction="in" type="i">
+ <doc:doc>
+ <doc:summary>The account type, encoded as an integer</doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Creates a new user account.
+ </doc:para>
+ <doc:para>
+ The accountType argument can take the following values:
+ </doc:para>
+ <doc:list>
+ <doc:item>
+ <doc:term>0</doc:term>
+ <doc:definition>Standard user</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>1</doc:term>
+ <doc:definition>Administrator</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:description>
+ <doc:permission>
+ The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization.
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="CacheUser">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="name" direction="in" type="s">
+ <doc:doc><doc:summary>The username for the user</doc:summary></doc:doc>
+ </arg>
+ <arg name="user" direction="out" type="o">
+ <doc:doc><doc:summary>Object path of user</doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Caches a user account, so that it shows up in ListCachedUsers() output.
+ The user name may be a remote user, but the system must be able to lookup
+ the user name and resolve the user information.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization.
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.UserDoesNotExist">if the user name cannot be
resolved</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="UncacheUser">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="name" direction="in" type="s">
+ <doc:doc><doc:summary>The username for the user</doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Releases all metadata about a user account, including icon, language and session. If the user
account is
+ from a remote server and the user has never logged in before, then that account will no longer
show up
+ in ListCachedUsers() output.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization.
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.UserDoesNotExist">if the user name cannot be
resolved</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <method name="DeleteUser">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="id" direction="in" type="x">
+ <doc:doc><doc:summary>The uid to delete</doc:summary></doc:doc>
+ </arg>
+ <arg name="removeFiles" direction="in" type="b">
+ <doc:doc><doc:summary>Whether to remove the users files</doc:summary></doc:doc>
+ </arg>
+
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Deletes a user account.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization.
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the
appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
+ <signal name="UserAdded">
+ <arg name="user" type="o">
+ <doc:doc><doc:summary>Object path of the user that was added.</doc:summary></doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Emitted when a user is added.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+
+ <signal name="UserDeleted">
+ <arg name="user" type="o">
+ <doc:doc><doc:summary>Object path of the user that was deleted.</doc:summary></doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Emitted when a user is deleted.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </signal>
+
+ <property name="DaemonVersion" type="s" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The version of the running daemon.
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
+ </interface>
+</node>
diff --git a/src/account.c b/src/account.c
new file mode 100644
index 0000000..372bdf1
--- /dev/null
+++ b/src/account.c
@@ -0,0 +1,260 @@
+#define _GNU_SOURCE 1
+
+#include "config.h"
+
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <gtk/gtk.h>
+
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+#include <gio/gunixfdlist.h>
+
+#include "xdg-desktop-portal-dbus.h"
+#include "shell-dbus.h"
+
+#include "account.h"
+#include "accountdialog.h"
+#include "request.h"
+#include "utils.h"
+#include "externalwindow.h"
+
+static OrgFreedesktopAccountsUser *user;
+
+typedef struct {
+ XdpImplAccount *impl;
+ GDBusMethodInvocation *invocation;
+ Request *request;
+
+ GtkWidget *dialog;
+ ExternalWindow *external_parent;
+
+ int response;
+ char *user_name;
+ char *real_name;
+ char *icon_uri;
+} AccountDialogHandle;
+
+static void
+account_dialog_handle_free (gpointer data)
+{
+ AccountDialogHandle *handle = data;
+
+ g_clear_object (&handle->external_parent);
+ g_object_unref (handle->request);
+ g_object_unref (handle->dialog);
+ g_free (handle->user_name);
+ g_free (handle->real_name);
+ g_free (handle->icon_uri);
+
+ g_free (handle);
+}
+
+static void
+account_dialog_handle_close (AccountDialogHandle *handle)
+{
+ gtk_window_destroy (GTK_WINDOW (handle->dialog));
+ account_dialog_handle_free (handle);
+}
+
+static void
+send_response (AccountDialogHandle *handle)
+{
+ GVariantBuilder opt_builder;
+
+ g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
+ g_variant_builder_add (&opt_builder, "{sv}", "id", g_variant_new_string (handle->user_name ?
handle->user_name : ""));
+ g_variant_builder_add (&opt_builder, "{sv}", "name", g_variant_new_string (handle->real_name ?
handle->real_name : ""));
+ g_variant_builder_add (&opt_builder, "{sv}", "image", g_variant_new_string (handle->icon_uri ?
handle->icon_uri : ""));
+
+ if (handle->request->exported)
+ request_unexport (handle->request);
+
+ xdp_impl_account_complete_get_user_information (handle->impl,
+ handle->invocation,
+ handle->response,
+ g_variant_builder_end (&opt_builder));
+
+ account_dialog_handle_close (handle);
+}
+
+static void
+account_dialog_done (GtkWidget *widget,
+ int response,
+ const char *user_name,
+ const char *real_name,
+ const char *icon_file,
+ gpointer user_data)
+{
+ AccountDialogHandle *handle = user_data;
+
+ g_clear_pointer (&handle->user_name, g_free);
+ g_clear_pointer (&handle->real_name, g_free);
+ g_clear_pointer (&handle->icon_uri, g_free);
+
+ switch (response)
+ {
+ default:
+ g_warning ("Unexpected response: %d", response);
+ /* Fall through */
+ case GTK_RESPONSE_DELETE_EVENT:
+ handle->response = 2;
+ break;
+
+ case GTK_RESPONSE_CANCEL:
+ handle->response = 1;
+ break;
+
+ case GTK_RESPONSE_OK:
+ handle->user_name = g_strdup (user_name);
+ handle->real_name = g_strdup (real_name);
+ if (icon_file)
+ handle->icon_uri = g_filename_to_uri (icon_file, NULL, NULL);
+ handle->response = 0;
+ break;
+ }
+
+ send_response (handle);
+}
+
+static gboolean
+handle_close (XdpImplRequest *object,
+ GDBusMethodInvocation *invocation,
+ AccountDialogHandle *handle)
+{
+ GVariantBuilder opt_builder;
+
+ g_variant_builder_init (&opt_builder, G_VARIANT_TYPE_VARDICT);
+ xdp_impl_account_complete_get_user_information (handle->impl,
+ handle->invocation,
+ 2,
+ g_variant_builder_end (&opt_builder));
+ account_dialog_handle_close (handle);
+
+ return FALSE;
+}
+
+static gboolean
+handle_get_user_information (XdpImplAccount *object,
+ GDBusMethodInvocation *invocation,
+ const char *arg_handle,
+ const char *arg_app_id,
+ const char *arg_parent_window,
+ GVariant *arg_options)
+{
+ g_autoptr(Request) request = NULL;
+ const char *sender;
+ AccountDialogHandle *handle;
+ const char *user_name;
+ const char *real_name;
+ const char *icon_file;
+ GtkWidget *dialog;
+ GdkSurface *surface;
+ GdkDisplay *display;
+ ExternalWindow *external_parent = NULL;
+ GtkWidget *fake_parent;
+ const char *reason;
+
+ sender = g_dbus_method_invocation_get_sender (invocation);
+
+ request = request_new (sender, arg_app_id, arg_handle);
+
+ user_name = org_freedesktop_accounts_user_get_user_name (user);
+ real_name = org_freedesktop_accounts_user_get_real_name (user);
+ icon_file = org_freedesktop_accounts_user_get_icon_file (user);
+
+ if (!g_variant_lookup (arg_options, "reason", "&s", &reason))
+ reason = NULL;
+
+ if (arg_parent_window)
+ {
+ external_parent = create_external_window_from_handle (arg_parent_window);
+ if (!external_parent)
+ g_warning ("Failed to associate portal window with parent window %s",
+ arg_parent_window);
+ }
+
+ if (external_parent)
+ display = external_window_get_display (external_parent);
+ else
+ display = gdk_display_get_default ();
+
+ fake_parent = g_object_new (GTK_TYPE_WINDOW,
+ "display", display,
+ NULL);
+ g_object_ref_sink (fake_parent);
+
+ dialog = GTK_WIDGET (account_dialog_new (arg_app_id, user_name, real_name, icon_file, reason));
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fake_parent));
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+ handle = g_new0 (AccountDialogHandle, 1);
+ handle->impl = object;
+ handle->invocation = invocation;
+ handle->request = g_object_ref (request);
+ handle->dialog = g_object_ref (dialog);
+ handle->external_parent = external_parent;
+ handle->user_name = g_strdup (user_name);
+ handle->real_name = g_strdup (real_name);
+ handle->icon_uri = g_filename_to_uri (icon_file, NULL, NULL);
+
+ g_signal_connect (request, "handle-close", G_CALLBACK (handle_close), handle);
+
+ g_signal_connect (dialog, "done", G_CALLBACK (account_dialog_done), handle);
+
+ gtk_widget_realize (dialog);
+
+ surface = gtk_native_get_surface (GTK_NATIVE (dialog));
+ if (external_parent)
+ external_window_set_parent_of (external_parent, surface);
+
+ gtk_widget_show (dialog);
+
+ request_export (request, g_dbus_method_invocation_get_connection (invocation));
+
+ return TRUE;
+}
+
+static GDBusConnection *system_bus;
+
+gboolean
+account_init (GDBusConnection *bus,
+ GError **error)
+{
+ GDBusInterfaceSkeleton *helper;
+ g_autofree char *object_path = NULL;
+
+ system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
+ if (system_bus == NULL)
+ return FALSE;
+
+ helper = G_DBUS_INTERFACE_SKELETON (xdp_impl_account_skeleton_new ());
+
+ g_signal_connect (helper, "handle-get-user-information", G_CALLBACK (handle_get_user_information), NULL);
+
+ if (!g_dbus_interface_skeleton_export (helper,
+ bus,
+ DESKTOP_PORTAL_OBJECT_PATH,
+ error))
+ return FALSE;
+
+ object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%d", getuid ());
+
+ user = org_freedesktop_accounts_user_proxy_new_sync (system_bus,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ "org.freedesktop.Accounts",
+ object_path,
+ NULL,
+ error);
+ if (user == NULL)
+ return FALSE;
+
+ g_debug ("providing %s", g_dbus_interface_skeleton_get_info (helper)->name);
+
+ return TRUE;
+}
diff --git a/src/account.h b/src/account.h
new file mode 100644
index 0000000..01f353d
--- /dev/null
+++ b/src/account.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2016 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Matthias Clasen <mclasen redhat com>
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+gboolean account_init (GDBusConnection *bus,
+ GError **error);
diff --git a/src/accountdialog.c b/src/accountdialog.c
new file mode 100644
index 0000000..6f5577e
--- /dev/null
+++ b/src/accountdialog.c
@@ -0,0 +1,252 @@
+#define _GNU_SOURCE 1
+
+#include <string.h>
+
+#include <adwaita.h>
+#include <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
+
+#include <glib/gi18n.h>
+
+#include "accountdialog.h"
+
+struct _AccountDialog {
+ GtkWindow parent;
+
+ GtkWidget *heading;
+ GtkWidget *accept_button;
+ GtkWidget *name;
+ GtkWidget *fullname;
+ AdwAvatar *image;
+
+ char *icon_file;
+};
+
+struct _AccountDialogClass {
+ GtkWindowClass parent_class;
+};
+
+enum {
+ DONE,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (AccountDialog, account_dialog, GTK_TYPE_WINDOW)
+
+static void
+account_dialog_init (AccountDialog *dialog)
+{
+ gtk_widget_init_template (GTK_WIDGET (dialog));
+}
+
+static void
+account_dialog_finalize (GObject *object)
+{
+ AccountDialog *dialog = ACCOUNT_DIALOG (object);
+
+ g_clear_pointer (&dialog->icon_file, g_free);
+
+ G_OBJECT_CLASS (account_dialog_parent_class)->finalize (object);
+}
+
+static gboolean
+account_dialog_close_request (GtkWindow *dialog)
+{
+ gtk_widget_hide (GTK_WIDGET (dialog));
+
+ g_signal_emit (dialog, signals[DONE], 0, GTK_RESPONSE_CANCEL, NULL);
+
+ return TRUE;
+}
+
+static void
+button_clicked (GtkWidget *button,
+ AccountDialog *dialog)
+{
+ int response;
+ const char *user_name;
+ const char *real_name;
+
+ gtk_widget_hide (GTK_WIDGET (dialog));
+
+ if (button == dialog->accept_button)
+ response = GTK_RESPONSE_OK;
+ else
+ response = GTK_RESPONSE_CANCEL;
+
+ user_name = gtk_editable_get_text (GTK_EDITABLE (dialog->name));
+ real_name = gtk_editable_get_text (GTK_EDITABLE (dialog->fullname));
+ g_signal_emit (dialog, signals[DONE], 0, response, user_name, real_name, dialog->icon_file);
+}
+
+static void
+dialog_set_icon_file (AccountDialog *dialog,
+ const char *icon_file)
+{
+ g_clear_pointer (&dialog->icon_file, g_free);
+ dialog->icon_file = g_strdup (icon_file);
+
+ if (icon_file)
+ {
+ g_autoptr(GdkTexture) texture = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GFile) file = NULL;
+
+ file = g_file_new_for_path (icon_file);
+ texture = gdk_texture_new_from_file (file, &error);
+ if (error)
+ g_warning ("Failed to load account %s: %s", icon_file, error->message);
+ adw_avatar_set_custom_image (dialog->image, GDK_PAINTABLE (texture));
+ gtk_widget_remove_css_class (GTK_WIDGET (dialog->image), "dim-label");
+ }
+ else
+ {
+ adw_avatar_set_custom_image (dialog->image, NULL);
+ adw_avatar_set_icon_name (dialog->image, "camera-photo-symbolic");
+ gtk_widget_add_css_class (GTK_WIDGET (dialog->image), "dim-label");
+ }
+}
+
+static void
+file_chooser_response (GtkWidget *widget,
+ int response,
+ gpointer user_data)
+{
+ AccountDialog *dialog = user_data;
+ g_autoptr (GFile) file = NULL;
+ g_autofree char *path = NULL;
+
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (widget));
+ path = file ? g_file_get_path (file) : NULL;
+
+ switch (response)
+ {
+ default:
+ g_warning ("Unexpected response: %d", response);
+ /* Fall through */
+ case GTK_RESPONSE_DELETE_EVENT:
+ case GTK_RESPONSE_CANCEL:
+ break;
+
+ case GTK_RESPONSE_OK:
+ dialog_set_icon_file (dialog, path);
+ break;
+
+ case GTK_RESPONSE_CLOSE:
+ dialog_set_icon_file (dialog, NULL);
+ break;
+ }
+
+ gtk_window_destroy (GTK_WINDOW (widget));
+}
+
+static void
+image_button_clicked (AccountDialog *dialog)
+{
+ GtkWidget *chooser;
+ GtkFileFilter *filter;
+
+ chooser = gtk_file_chooser_dialog_new (_("Select an Image"),
+ GTK_WINDOW (dialog),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ _("Cancel"), GTK_RESPONSE_CANCEL,
+ _("Select"), GTK_RESPONSE_OK,
+ _("Clear"), GTK_RESPONSE_CLOSE,
+ NULL);
+ gtk_window_set_modal (GTK_WINDOW (chooser), TRUE);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK);
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("Images"));
+ gtk_file_filter_add_pixbuf_formats (filter);
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
+
+ g_signal_connect (chooser, "response", G_CALLBACK (file_chooser_response), dialog);
+
+ gtk_widget_show (chooser);
+}
+
+static void
+account_dialog_class_init (AccountDialogClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ GtkWindowClass *window_class = GTK_WINDOW_CLASS (class);
+
+ object_class->finalize = account_dialog_finalize;
+
+ window_class->close_request = account_dialog_close_request;
+
+ signals[DONE] = g_signal_new ("done",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 4,
+ G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
"/org/freedesktop/portal/desktop/gnome/accountdialog.ui");
+ gtk_widget_class_bind_template_child (widget_class, AccountDialog, accept_button);
+ gtk_widget_class_bind_template_child (widget_class, AccountDialog, heading);
+ gtk_widget_class_bind_template_child (widget_class, AccountDialog, name);
+ gtk_widget_class_bind_template_child (widget_class, AccountDialog, fullname);
+ gtk_widget_class_bind_template_child (widget_class, AccountDialog, image);
+
+ gtk_widget_class_bind_template_callback (widget_class, button_clicked);
+ gtk_widget_class_bind_template_callback (widget_class, image_button_clicked);
+}
+
+AccountDialog *
+account_dialog_new (const char *app_id,
+ const char *user_name,
+ const char *real_name,
+ const char *icon_file,
+ const char *reason)
+{
+ AccountDialog *dialog;
+ g_autofree char *heading = NULL;
+ g_autoptr(GdkTexture) texture = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GFile) file = NULL;
+
+ dialog = g_object_new (account_dialog_get_type (), NULL);
+
+ if (strcmp (app_id, "") != 0)
+ {
+ g_autofree char *id = NULL;
+ g_autoptr(GAppInfo) info = NULL;
+ id = g_strconcat (app_id, ".desktop", NULL);
+ info = G_APP_INFO (g_desktop_app_info_new (id));
+ heading = g_strdup_printf (_("Share your personal information with %1$s? %2$s"),
+ g_app_info_get_display_name (info),
+ reason ?: "");
+ }
+ else
+ {
+ heading = g_strdup_printf (_("Share your personal information with the requesting application? %s"),
+ reason ?: "");
+ }
+
+ gtk_label_set_label (GTK_LABEL (dialog->heading), heading);
+ gtk_editable_set_text (GTK_EDITABLE (dialog->name), user_name);
+ gtk_editable_set_text (GTK_EDITABLE (dialog->fullname), real_name);
+
+ file = g_file_new_for_path (icon_file);
+ texture = gdk_texture_new_from_file (file, &error);
+ if (error)
+ {
+ g_warning ("Failed to load account %s: %s", icon_file, error->message);
+ }
+ else
+ {
+ adw_avatar_set_custom_image (dialog->image, GDK_PAINTABLE (texture));
+ }
+
+ dialog->icon_file = g_strdup (icon_file);
+
+ return dialog;
+}
diff --git a/src/accountdialog.h b/src/accountdialog.h
new file mode 100644
index 0000000..e209343
--- /dev/null
+++ b/src/accountdialog.h
@@ -0,0 +1,17 @@
+#include <gtk/gtk.h>
+
+#define ACCOUNT_TYPE_DIALOG (account_dialog_get_type ())
+#define ACCOUNT_DIALOG(object) (G_TYPE_CHECK_INSTANCE_CAST (object, ACCOUNT_TYPE_DIALOG, AccountDialog))
+
+typedef struct _AccountDialog AccountDialog;
+typedef struct _AccountDialogClass AccountDialogClass;
+
+AccountDialog * account_dialog_new (const char *app_id,
+ const char *user_name,
+ const char *real_name,
+ const char *icon_file,
+ const char *reason);
+
+const char *account_dialog_get_user_name (AccountDialog *dialog);
+const char *account_dialog_get_real_name (AccountDialog *dialog);
+const char *account_dialog_get_icon_file (AccountDialog *dialog);
diff --git a/src/accountdialog.ui b/src/accountdialog.ui
new file mode 100644
index 0000000..581e9c3
--- /dev/null
+++ b/src/accountdialog.ui
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="xdg-desktop-portal-gnome">
+ <template class="AccountDialog" parent="GtkWindow">
+ <property name="title" translatable="yes">Share Details</property>
+ <property name="resizable">0</property>
+
+ <child type="titlebar">
+ <object class="GtkHeaderBar" id="titlebar">
+ <property name="show-title-buttons">False</property>
+ <child>
+ <object class="GtkButton" id="cancel_button">
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="use-underline">1</property>
+ <signal name="clicked" handler="button_clicked"/>
+ </object>
+ </child>
+ <child type="end">
+ <object class="GtkButton" id="accept_button">
+ <property name="label" translatable="yes">_Share</property>
+ <property name="use-underline">1</property>
+ <signal name="clicked" handler="button_clicked"/>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="margin-top">24</property>
+ <property name="margin-bottom">24</property>
+ <property name="margin-start">36</property>
+ <property name="margin-end">36</property>
+ <property name="spacing">24</property>
+
+ <child>
+ <object class="GtkLabel" id="heading">
+ <property name="wrap">1</property>
+ <property name="xalign">0.5</property>
+ <property name="max-width-chars">32</property>
+ <property name="width-chars">32</property>
+ </object>
+ </child>
+
+ <!-- Profile Picture -->
+ <child>
+ <object class="GtkButton" id="button">
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <signal name="clicked" handler="image_button_clicked" swapped="yes"/>
+ <child>
+ <object class="AdwAvatar" id="image">
+ <property name="size">96</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <!-- Rows -->
+ <child>
+ <object class="GtkListBox">
+ <property name="selection-mode">none</property>
+ <style>
+ <class name="content" />
+ </style>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Name</property>
+ <property name="activatable-widget">fullname</property>
+ <child>
+ <object class="GtkEntry" id="fullname">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ <child>
+ <object class="AdwActionRow">
+ <property name="title" translatable="yes">Username</property>
+ <property name="activatable-widget">name</property>
+ <child>
+ <object class="GtkEntry" id="name">
+ <property name="valign">center</property>
+ </object>
+ </child>
+ </object>
+ </child>
+
+ </object>
+ </child>
+
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/meson.build b/src/meson.build
index f26185c..e38787f 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -6,6 +6,7 @@ xdg_desktop_portal_dep = dependency('xdg-desktop-portal', version: '>= 1.5')
# Desktop Portal D-Bus interfaces
desktop_portal_interfaces_dir = xdg_desktop_portal_dep.get_pkgconfig_variable('interfaces_dir')
desktop_portal_dbus_interfaces = [
+ desktop_portal_interfaces_dir / 'org.freedesktop.impl.portal.Account.xml',
desktop_portal_interfaces_dir / 'org.freedesktop.impl.portal.Print.xml',
desktop_portal_interfaces_dir / 'org.freedesktop.impl.portal.Request.xml',
desktop_portal_interfaces_dir / 'org.freedesktop.impl.portal.Session.xml',
@@ -30,6 +31,8 @@ built_sources = gnome.gdbus_codegen(
# Mutter / GNOME Shell D-Bus interfaces
top_srcdir = meson.source_root()
shell_dbus_interfaces = files(
+ top_srcdir / 'data' / 'org.freedesktop.Accounts.User.xml',
+ top_srcdir / 'data' / 'org.freedesktop.Accounts.xml',
top_srcdir / 'data' / 'org.gnome.Mutter.DisplayConfig.xml',
top_srcdir / 'data' / 'org.gnome.Mutter.RemoteDesktop.xml',
top_srcdir / 'data' / 'org.gnome.Mutter.ScreenCast.xml',
@@ -78,6 +81,8 @@ deps = [
]
sources = built_sources + files(
+ 'account.c',
+ 'accountdialog.c',
'appchooser.c',
'appchooserrow.c',
'appchooserdialog.c',
diff --git a/src/xdg-desktop-portal-gnome.c b/src/xdg-desktop-portal-gnome.c
index e896195..6370b7c 100644
--- a/src/xdg-desktop-portal-gnome.c
+++ b/src/xdg-desktop-portal-gnome.c
@@ -42,6 +42,7 @@
#include "xdg-desktop-portal-dbus.h"
+#include "account.h"
#include "appchooser.h"
#include "background.h"
#include "filechooser.h"
@@ -110,6 +111,12 @@ on_bus_acquired (GDBusConnection *connection,
g_clear_error (&error);
}
+ if (!account_init (connection, &error))
+ {
+ g_warning ("error: %s\n", error->message);
+ g_clear_error (&error);
+ }
+
if (!app_chooser_init (connection, &error))
{
g_warning ("error: %s\n", error->message);
diff --git a/src/xdg-desktop-portal-gnome.gresource.xml b/src/xdg-desktop-portal-gnome.gresource.xml
index 12cc0b0..aee9b53 100644
--- a/src/xdg-desktop-portal-gnome.gresource.xml
+++ b/src/xdg-desktop-portal-gnome.gresource.xml
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<gresources>
<gresource prefix='/org/freedesktop/portal/desktop/gnome'>
+ <file>accountdialog.ui</file>
<file>appchooserdialog.ui</file>
<file>appchooserrow.ui</file>
<file>screenshotdialog.ui</file>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]