diff --git a/src/AppSystem/App.vala b/src/AppSystem/App.vala index 5c1f8e8d..7bf38fc3 100644 --- a/src/AppSystem/App.vala +++ b/src/AppSystem/App.vala @@ -137,6 +137,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); @@ -152,6 +154,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 + 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");