Skip to content
89 changes: 89 additions & 0 deletions src/AppSystem/App.vala
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ public class Dock.App : Object {
switcheroo_control.apply_gpu_environment (context, prefers_nondefault_gpu);
}

context.launched.connect (start_systemd_scope);

try {
if (action != null) {
app_info.launch_action (action, context);
Expand All @@ -157,6 +159,93 @@ public class Dock.App : Object {
}
}

private async void start_systemd_scope (AppLaunchContext context, GLib.AppInfo appinfo, Variant platform_data) {
int pid;
if (!platform_data.lookup ("pid", "i", out pid)) {
return;
}

var app_name = appinfo.get_id () ?? appinfo.get_executable ();
// if we got a path, use the binary name only
if (app_name.has_prefix ("/")) {
app_name = Path.get_basename (app_name);
} else if (app_name.has_suffix (".desktop")) {
app_name = app_name.substring (0, app_name.length - 8);
}

DBusConnection connection;
try {
connection = Bus.get_sync (SESSION, null);
} catch (Error e) {
critical ("Couldn't connect to DBus: %s", e.message);
return;
}

// If an app is dbus activatable, we don't launch it directly
// dbus-daemon launches it for us, so we can't get pid from platform_data
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we can't get pid from platform_data in this circumstance, what would the lookup call at the beginning of this function return?

if (appinfo is DesktopAppInfo && ((DesktopAppInfo) appinfo).get_boolean ("DBusActivatable")) {
try {
var reply = connection.call_sync (
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"GetConnectionUnixProcessID",
new Variant ("(s)", app_name),
new VariantType ("(u)"),
NONE,
1000,
null
);

reply.get ("(u)", out pid);
} catch (Error e) {
critical ("Couldn't get pid of dbus activatable app: %s", e.message);
return;
}
}

var string_builder = new StringBuilder.sized (app_name.length);
for (var i = 0; i < app_name.length; i++) {
var c = app_name[i];
if (c.isalnum () || c == ':' || c == '_' || c == '.') {
string_builder.append_c (c);
} else {
string_builder.append_printf ("\\x%02x", c);
}
}

var builder = new VariantBuilder (new VariantType ("(ssa(sv)a(sa(sv)))"));
builder.add ("s", "app-pantheon-%s-%d.scope".printf (string_builder.free_and_steal (), pid));
builder.add ("s", "fail");

builder.open (new VariantType ("a(sv)"));
builder.add ("(sv)", "Description", new Variant.string ("Application launched by %s".printf (GLib.Application.get_default ().application_id)));
builder.add ("(sv)", "PIDs", new Variant.array (VariantType.UINT32, { (uint32) pid }));

// Default to let systemd garbage collect failed applications we launched.
builder.add ("(sv)", "CollectMode", new Variant.string ("inactive-or-failed"));
builder.close ();

builder.open (new VariantType ("a(sa(sv))"));
builder.close ();

try {
connection.call_sync (
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit",
builder.end (),
new VariantType ("(o)"),
NO_AUTO_START,
1000,
null
);
} catch (Error e) {
warning ("Couldn't put an app into Systemd scope: %s", e.message);
}
}

public bool launch_new_instance (AppLaunchContext context) {
// Treat this as a string to distinguish between false and null
var single_main_window = app_info.get_string ("SingleMainWindow");
Expand Down
Loading