Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Background portal implementation #73

Merged
merged 39 commits into from
May 22, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
fe1fb09
Prototype background portal
leolost2605 May 12, 2023
913c895
Minor fixes
leolost2605 May 12, 2023
dccd199
Cleanup
leolost2605 May 12, 2023
dbb9c93
Add fixmes
leolost2605 May 12, 2023
293c4e4
Fix lint
leolost2605 May 12, 2023
884429c
Fix criticals, implement NotifyBackground
leolost2605 May 13, 2023
d01480b
Update ci
leolost2605 May 13, 2023
70aba7b
Fix quoting of args that don't need quoting
leolost2605 May 14, 2023
90d7d8a
Use first commandline arg as app_id if it is empty
leolost2605 May 14, 2023
030d076
Cleanup, use enums
leolost2605 May 14, 2023
87a5bf5
Cleanup
leolost2605 May 14, 2023
bf2613c
Remove .desktop
leolost2605 May 14, 2023
891ea0a
Init Notify with xdg-desktop-portal-pantheon
leolost2605 May 14, 2023
4701a54
Apply suggestions from code review
leolost2605 May 17, 2023
a3c2033
Fix stuff from code review
leolost2605 May 17, 2023
fc627b4
Always use first command_line arg as app_id if it is empty
leolost2605 May 17, 2023
f95c7ee
Handle notifications without libnotify
leolost2605 May 17, 2023
1f5116a
Rename variable
leolost2605 May 17, 2023
88db5f8
Don't keep references on expired notifications
leolost2605 May 17, 2023
6cb7ea5
Merge branch 'main' into background-portal
zeebok May 18, 2023
17bc48a
Apply suggestions from code review
leolost2605 May 18, 2023
13cdb0e
Merge NotificationHandler and NotificationRequest
May 18, 2023
732c8c0
Add license header
leolost2605 May 18, 2023
8d0e6e1
Change notification content
leolost2605 May 18, 2023
61fce13
Add failed result, set response
leolost2605 May 18, 2023
5b4cc60
Formatting
leolost2605 May 18, 2023
c566a4b
Cleanup
leolost2605 May 19, 2023
3dabd28
Merge branch 'main' into background-portal
zeebok May 21, 2023
1f7dd77
Apply suggestions from code review
leolost2605 May 22, 2023
6b6ec9e
Fix issues from code review
leolost2605 May 22, 2023
8810517
Apply suggestions from code review
leolost2605 May 22, 2023
ed1d592
Use AppInfo to get name + icon, use all commandline args as filename
leolost2605 May 22, 2023
d8eb4ac
Fix lint
leolost2605 May 22, 2023
f06b991
Add some dbug messages and fix issue with the appstate variant not be…
leolost2605 May 22, 2023
67004b1
Add another debug message
leolost2605 May 22, 2023
c851f0d
Fix autostart
leolost2605 May 22, 2023
72eea6e
Cleanup
leolost2605 May 22, 2023
f29b0ae
Cleanup
leolost2605 May 22, 2023
14d29bb
Use Flags
leolost2605 May 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: Install Dependencies
run: |
apt update
apt install -y libgranite-7-dev libgtk-4-dev meson valac
apt install -y libgranite-7-dev libgtk-4-dev libnotify-dev meson valac
- name: Build
run: |
meson build
Expand Down
2 changes: 1 addition & 1 deletion data/pantheon.portal
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[portal]
DBusName=org.freedesktop.impl.portal.desktop.pantheon
Interfaces=org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.AppChooser;
Interfaces=org.freedesktop.impl.portal.Access;org.freedesktop.impl.portal.AppChooser;org.freedesktop.impl.portal.Background;
UseIn=pantheon
1 change: 1 addition & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ glib_dep = dependency('glib-2.0')
gobject_dep = dependency('gobject-2.0')
gio_dep = dependency('gio-2.0')
granite_dep = dependency('granite-7')
notify_dep = dependency('libnotify')

gtk_deps = [
dependency('gtk4')
Expand Down
199 changes: 199 additions & 0 deletions src/Background/Portal.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* SPDX-FileCopyrigthText: 2021 elementary, Inc. (https://elementary.io)
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved
* SPDX-License-Identifier: LGPL-2.1-or-later
*/

[DBus (name = "org.freedesktop.impl.portal.Background")]
public class Background.Portal : Object {
public signal void running_applications_changed ();

private const string ACTION_ALLOW_BACKGROUND = "background.allow";
private const string ACTION_FORBID_BACKGROUND = "background.forbid";

private DBusConnection connection;
private DesktopIntegration desktop_integration;

public Portal (DBusConnection connection) {
this.connection = connection;
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved
}

public struct RunningApplication {
string app_id;
GLib.HashTable<string, Variant> details;
}

[DBus (name = "org.pantheon.gala.DesktopIntegration")]
public interface DesktopIntegration : GLib.Object {
[DBus (name = "RunningApplicationsChanged")]
public abstract signal void integration_running_applications_changed ();
[DBus (name = "GetRunningApplications")]
public abstract void get_running_applications (out RunningApplication[] running_apps) throws DBusError, IOError;
}
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved

construct {
try {
desktop_integration = Bus.get_proxy_sync (BusType.SESSION, "org.pantheon.gala", "/org/pantheon/gala/DesktopInterface");
desktop_integration.integration_running_applications_changed.connect (() => running_applications_changed ());
} catch (Error e) {
critical (e.message);
}
}

private enum WindowState {
BACKGROUND = 0, //No open window
RUNNING = 1, //At least one open window
ACTIVE = 2 //In the foreground
}
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved

public void get_app_state (out HashTable<string, Variant> apps) throws DBusError, IOError {
apps = new HashTable<string, Variant> (str_hash, str_equal);

try {
RunningApplication[] result = {};
desktop_integration.get_running_applications (out result);
for (int i = 0; i < result.length; i++) {
var app_id = result[i].app_id.strip ();
if (app_id.has_suffix (".desktop")) {
var index = app_id.last_index_of (".desktop");
app_id = app_id.slice (0, index);
}
apps.set (app_id, WindowState.RUNNING); //FIXME: Don't hardcode: needs implementation on the gala side
}
} catch (Error e) {
critical (e.message);
}
}
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved

private enum NotifyBackgroundResult {
FORBID = 0,
ALLOW = 1,
ALLOW_ONCE = 2
}

public async void notify_background (
ObjectPath handle,
string app_id,
string name,
out uint32 response,
out HashTable<string, Variant> results
) throws DBusError, IOError {
var _results = new HashTable<string, Variant> (str_hash, str_equal);

var notification = new Notify.Notification (
_("Background activity"),
_(""""%s" is running in the background""").printf (name),
"dialog-information"
);

notification.add_action (ACTION_ALLOW_BACKGROUND, _("Allow"), () => {
_results.set ("result", NotifyBackgroundResult.ALLOW);
notify_background.callback ();
});

notification.add_action (ACTION_FORBID_BACKGROUND, _("Forbid"), () => {
_results.set ("result", NotifyBackgroundResult.FORBID);
notify_background.callback ();
});

notification.closed.connect (() => {
_results.set ("result", NotifyBackgroundResult.ALLOW_ONCE);
notify_background.callback ();
});

try {
notification.show ();
} catch (Error e) {
critical ("Failed to send background notification for %s: %s", app_id, e.message);
}

yield;

response = 0; //Won't be used
results = _results;
}

private enum AutostartFlags {
AUTOSTART_FLAGS_NONE = 0,
AUTOSTART_FLAGS_DBUS_ACTIVATABLE = 1
}

public void enable_autostart (
string app_id,
bool enable,
string[] commandline,
uint32 flags,
out bool result
) throws DBusError, IOError {
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved
result = false;

/* If the portal request is made by a non-flatpaked application app_id will most of the time be empty */
if (app_id.strip () == "") {
/* Usually we can then asume that the first commandline arg is the app_id
but just to be sure we only do this with our own (io.elementary.APP) ones.
The reason we do this at all are primarily mail, calendar and tasks, which need to autostart
but currently can't be shipped as flatpaks, so this is useful to not have to care about that stuff
in the respective apps and even allow user intervention */
if (commandline[0].contains ("io.elementary.")) {
app_id = commandline[0];
} else {
return;
}
}

string file_name = app_id + ".desktop";
string directory = Path.build_filename (Environment.get_user_config_dir (), "autostart");
string full_path = Path.build_filename (directory, file_name);
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved

if (!enable) {
FileUtils.unlink (full_path);
return;
}

var autostart_flags = (AutostartFlags) flags;

var key_file = new KeyFile ();
key_file.set_string (KeyFileDesktop.GROUP, KeyFileDesktop.KEY_TYPE, "Application");
key_file.set_string (KeyFileDesktop.GROUP, KeyFileDesktop.KEY_NAME, app_id);
key_file.set_string (KeyFileDesktop.GROUP, KeyFileDesktop.KEY_EXEC, flatpak_quote_argv (commandline));
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved
if (autostart_flags == AUTOSTART_FLAGS_DBUS_ACTIVATABLE) {
key_file.set_boolean (KeyFileDesktop.GROUP, KeyFileDesktop.KEY_DBUS_ACTIVATABLE, true);
}
key_file.set_string (KeyFileDesktop.GROUP, "X-Flatpak", app_id);
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved

try {
key_file.save_to_file (full_path);
} catch (Error e) {
critical (e.message);
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved
return;
}

result = true;
}

private string flatpak_quote_argv (string[] argv) {
var builder = new StringBuilder ();

for (int i = 0; i < argv.length; i++) {
if (i != 0) {
builder.append (" ");
}

var str = argv[i];

for (int j = 0; j < str.char_count (); j++) {
char c = str.get (str.index_of_nth_char (j));
if (!c.isalnum () &&
!(c == '-' || c == '/' || c == '~' ||
c == ':' || c == '.' || c == '_' ||
c == '=' || c == '@')) {
str = Shell.quote (str);
break;
}
}

builder.append (str);
}

return builder.str;
leolost2605 marked this conversation as resolved.
Show resolved Hide resolved
}
}
4 changes: 4 additions & 0 deletions src/XdgDesktopPortalPantheon.vala
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ private void on_bus_acquired (DBusConnection connection, string name) {

connection.register_object ("/org/freedesktop/portal/desktop", new AppChooser.Portal (connection));
debug ("AppChooser Portal registered!");

connection.register_object ("/org/freedesktop/portal/desktop", new Background.Portal (connection));
debug ("Background Portal registered!");
} catch (Error e) {
critical ("Unable to register the object: %s", e.message);
}
Expand Down Expand Up @@ -76,6 +79,7 @@ int main (string[] args) {
GLib.Environment.unset_variable ("GTK_USE_PORTAL");

Gtk.init ();
Notify.init ("xdg-desktop-portal-pantheon");

try {
var opt_context = new OptionContext ("- portal backends");
Expand Down
2 changes: 2 additions & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ executable(
'AppChooser/AppButton.vala',
'AppChooser/Dialog.vala',
'AppChooser/Portal.vala',
'Background/Portal.vala',
configure_file(input: 'Config.vala.in', output: '@BASENAME@', configuration: conf_data),
'ExternalWindow.vala',
'XdgDesktopPortalPantheon.vala',
Expand All @@ -15,6 +16,7 @@ executable(
gio_dep,
granite_dep,
gtk_deps,
notify_dep,
x11_dep
],
install: true,
Expand Down