From 9cbf8e6c13cf8ba79fd48e889a1ee08de7e73635 Mon Sep 17 00:00:00 2001 From: lenemter Date: Wed, 8 Jul 2026 01:48:20 +0300 Subject: [PATCH 1/2] Put Greeter in a separate group --- lib/WindowManager.vala | 1 + protocol/pantheon-desktop-shell-v1.xml | 26 ++++++++ protocol/pantheon-desktop-shell.vapi | 16 ++++- src/PantheonShell.vala | 78 +++++++++++++++++++++++ src/ShellClients/ShellClientsManager.vala | 50 +++++++++++++++ src/Widgets/LockScreen.vala | 5 +- src/WindowManager.vala | 1 + 7 files changed, 174 insertions(+), 3 deletions(-) diff --git a/lib/WindowManager.vala b/lib/WindowManager.vala index 705df6bc5..bcc682326 100644 --- a/lib/WindowManager.vala +++ b/lib/WindowManager.vala @@ -40,6 +40,7 @@ namespace Gala { public enum WindowGroup { DESKTOP_SHELL, + LOCK_SCREEN_APPLICATION, LOCK_SCREEN, LOCK_SCREEN_SHELL, MODAL, diff --git a/protocol/pantheon-desktop-shell-v1.xml b/protocol/pantheon-desktop-shell-v1.xml index 2f1d92a74..8ed3ae144 100644 --- a/protocol/pantheon-desktop-shell-v1.xml +++ b/protocol/pantheon-desktop-shell-v1.xml @@ -28,6 +28,14 @@ + + + Create a greeter surface from an existing surface. + + + + + Create a desktop-specific surface from an existing surface. @@ -35,6 +43,14 @@ + + + + Create a greeter surface from an existing surface. + + + + @@ -128,6 +144,16 @@ + + + + + + Init Greeter + + + + diff --git a/protocol/pantheon-desktop-shell.vapi b/protocol/pantheon-desktop-shell.vapi index f7ddc93ba..364801de9 100644 --- a/protocol/pantheon-desktop-shell.vapi +++ b/protocol/pantheon-desktop-shell.vapi @@ -1,5 +1,5 @@ /* - * Copyright 2023 elementary, Inc. + * Copyright 2023-2026 elementary, Inc. * Copyright 2023 Corentin Noël * SPDX-License-Identifier: GPL-3.0-or-later */ @@ -12,7 +12,7 @@ namespace Pantheon.Desktop { public Pantheon.Desktop.GetPanel get_panel; public Pantheon.Desktop.GetWidget get_widget; public Pantheon.Desktop.GetExtendedBehavior get_extended_behavior; - + public Pantheon.Desktop.GetGreeter get_greeter; } [CCode (cheader_filename = "pantheon-desktop-shell-server-protocol.h", cname = "enum io_elementary_pantheon_panel_v1_anchor", cprefix="IO_ELEMENTARY_PANTHEON_PANEL_V1_ANCHOR_", has_type_id = false)] @@ -53,6 +53,14 @@ namespace Pantheon.Desktop { public Destroy destroy; } + [CCode (cheader_filename = "pantheon-desktop-shell-server-protocol.h", cname = "struct io_elementary_pantheon_greeter_v1_interface")] + public struct GreeterInterface { + [CCode (cheader_filename = "pantheon-desktop-shell-server-protocol.h", cname = "io_elementary_pantheon_greeter_v1_interface")] + public static Wl.Interface iface; + public Destroy destroy; + public Init init; + } + [CCode (cheader_filename = "pantheon-desktop-shell-server-protocol.h", cname = "struct io_elementary_pantheon_extended_behavior_v1_interface")] public struct ExtendedBehaviorInterface { [CCode (cheader_filename = "pantheon-desktop-shell-server-protocol.h", cname = "io_elementary_pantheon_extended_behavior_v1_interface")] @@ -70,6 +78,8 @@ namespace Pantheon.Desktop { [CCode (has_target = false, has_typedef = false)] public delegate void GetWidget (Wl.Client client, Wl.Resource resource, uint32 output, Wl.Resource surface); [CCode (has_target = false, has_typedef = false)] + public delegate void GetGreeter (Wl.Client client, Wl.Resource resource, uint32 output, Wl.Resource surface); + [CCode (has_target = false, has_typedef = false)] public delegate void GetExtendedBehavior (Wl.Client client, Wl.Resource resource, uint32 output, Wl.Resource surface); [CCode (has_target = false, has_typedef = false)] public delegate void SetAnchor (Wl.Client client, Wl.Resource resource, [CCode (type = "uint32_t")] Anchor anchor); @@ -86,6 +96,8 @@ namespace Pantheon.Desktop { [CCode (has_target = false, has_typedef = false)] public delegate void RemoveBlur (Wl.Client client, Wl.Resource resource); [CCode (has_target = false, has_typedef = false)] + public delegate void Init (Wl.Client client, Wl.Resource resource); + [CCode (has_target = false, has_typedef = false)] public delegate void SetKeepAbove (Wl.Client client, Wl.Resource resource); [CCode (has_target = false, has_typedef = false)] public delegate void MakeCentered (Wl.Client client, Wl.Resource resource); diff --git a/src/PantheonShell.vala b/src/PantheonShell.vala index 46136696d..af82298ab 100644 --- a/src/PantheonShell.vala +++ b/src/PantheonShell.vala @@ -17,6 +17,7 @@ namespace Gala { private static Pantheon.Desktop.ShellInterface wayland_pantheon_shell_interface; private static Pantheon.Desktop.PanelInterface wayland_pantheon_panel_interface; private static Pantheon.Desktop.WidgetInterface wayland_pantheon_widget_interface; + private static Pantheon.Desktop.GreeterInterface wayland_pantheon_greeter_interface; private static Pantheon.Desktop.ExtendedBehaviorInterface wayland_pantheon_extended_behavior_interface; private static Wl.Global shell_global; @@ -30,7 +31,9 @@ namespace Gala { wayland_pantheon_shell_interface = { get_panel, get_widget, + get_greeter, get_extended_behavior, + get_greeter, }; wayland_pantheon_panel_interface = { @@ -48,6 +51,11 @@ namespace Gala { destroy_widget_surface, }; + wayland_pantheon_greeter_interface = { + destroy_greeter_surface, + init_greeter, + }; + wayland_pantheon_extended_behavior_interface = { destroy_extended_behavior_surface, set_keep_above, @@ -59,6 +67,7 @@ namespace Gala { PanelSurface.quark = GLib.Quark.from_string ("-gala-wayland-panel-surface-data"); WidgetSurface.quark = GLib.Quark.from_string ("-gala-wayland-widget-surface-data"); + GreeterSurface.quark = GLib.Quark.from_string ("-gala-wayland-greeter-surface-data"); ExtendedBehaviorSurface.quark = GLib.Quark.from_string ("-gala-wayland-extended-behavior-surface-data"); shell_global = Wl.Global.create (wl_disp, ref Pantheon.Desktop.ShellInterface.iface, 1, (client, version, id) => { @@ -105,6 +114,25 @@ namespace Gala { } } + public class GreeterSurface : GLib.Object { + public static GLib.Quark quark = 0; + public unowned GLib.Object? wayland_surface; + + public GreeterSurface (GLib.Object wayland_surface) { + this.wayland_surface = wayland_surface; + } + + ~GreeterSurface () { + if (wayland_surface != null) { + wayland_surface.steal_qdata (quark); + } + } + + public void on_wayland_surface_disposed () { + wayland_surface = null; + } + } + public class ExtendedBehaviorSurface : GLib.Object { public static GLib.Quark quark = 0; public unowned GLib.Object? wayland_surface; @@ -186,6 +214,35 @@ namespace Gala { ); } + internal static void get_greeter (Wl.Client client, Wl.Resource resource, uint32 output, Wl.Resource surface_resource) { + unowned GLib.Object? wayland_surface = surface_resource.get_user_data (); + GreeterSurface? greeter_surface = wayland_surface.get_qdata (GreeterSurface.quark); + if (greeter_surface != null) { + surface_resource.post_error ( + Wl.DisplayError.INVALID_OBJECT, + "io_elementary_pantheon_shell_v1_interface::get_greeter already requested" + ); + return; + } + + greeter_surface = new GreeterSurface (wayland_surface); + unowned var greeter_resource = client.create_resource ( + ref Pantheon.Desktop.GreeterInterface.iface, + resource.get_version (), + output + ); + greeter_resource.set_implementation ( + &wayland_pantheon_greeter_interface, + greeter_surface.ref (), + unref_obj_on_destroy + ); + wayland_surface.set_qdata_full ( + GreeterSurface.quark, + greeter_surface, + (GLib.DestroyNotify) GreeterSurface.on_wayland_surface_disposed + ); + } + internal static void get_extended_behavior (Wl.Client client, Wl.Resource resource, uint32 output, Wl.Resource surface_resource) { unowned GLib.Object? wayland_surface = surface_resource.get_user_data (); ExtendedBehaviorSurface? eb_surface = wayland_surface.get_qdata (ExtendedBehaviorSurface.quark); @@ -348,6 +405,23 @@ namespace Gala { BlurManager.get_instance ().remove_blur (window); } + internal static void init_greeter (Wl.Client client, Wl.Resource resource) { + unowned GreeterSurface? greeter_surface = resource.get_user_data (); + if (greeter_surface.wayland_surface == null) { + warning ("Window tried to init greeter but wayland surface is null."); + return; + } + + Meta.Window? window; + greeter_surface.wayland_surface.get ("window", out window, null); + if (window == null) { + warning ("Window tried to init greeter but wayland surface had no associated window."); + return; + } + + ShellClientsManager.get_instance ().init_greeter (window); + } + internal static void set_keep_above (Wl.Client client, Wl.Resource resource) { unowned ExtendedBehaviorSurface? eb_surface = resource.get_user_data (); if (eb_surface.wayland_surface == null) { @@ -416,6 +490,10 @@ namespace Gala { resource.destroy (); } + internal static void destroy_greeter_surface (Wl.Client client, Wl.Resource resource) { + resource.destroy (); + } + internal static void destroy_extended_behavior_surface (Wl.Client client, Wl.Resource resource) { resource.destroy (); } diff --git a/src/ShellClients/ShellClientsManager.vala b/src/ShellClients/ShellClientsManager.vala index bcaf1d4a2..19429ac22 100644 --- a/src/ShellClients/ShellClientsManager.vala +++ b/src/ShellClients/ShellClientsManager.vala @@ -182,6 +182,50 @@ public class Gala.ShellClientsManager : Object, GestureTarget { } #endif + public void make_desktop (Meta.Window window) { +#if HAS_MUTTER49 + window.set_type (Meta.WindowType.DESKTOP); +#else + if (Meta.Util.is_wayland_compositor ()) { + make_desktop_wayland (window); + } else { + make_desktop_x11 (window); + } +#endif + } + +#if !HAS_MUTTER49 + private void make_desktop_wayland (Meta.Window window) requires (Meta.Util.is_wayland_compositor ()) { + foreach (var client in protocol_clients) { + if (client.wayland_client.owns_window (window)) { +#if HAS_MUTTER46 + client.wayland_client.make_desktop (window); +#endif + break; + } + } + } + + private void make_desktop_x11 (Meta.Window window) requires (!Meta.Util.is_wayland_compositor ()) { + unowned var x11_display = wm.get_display ().get_x11_display (); + +#if HAS_MUTTER46 + var x_window = x11_display.lookup_xwindow (window); +#else + var x_window = window.get_xwindow (); +#endif + // gtk3's gdk_x11_window_set_type_hint() is used as a reference + unowned var xdisplay = x11_display.get_xdisplay (); + var atom = xdisplay.intern_atom ("_NET_WM_WINDOW_TYPE", false); + var dock_atom = xdisplay.intern_atom ("_NET_WM_WINDOW_TYPE_DESKTOP", false); + + // (X.Atom) 4 is XA_ATOM + // 32 is format + // 0 means replace + xdisplay.change_property (x_window, atom, (X.Atom) 4, 32, 0, (uchar[]) dock_atom, 1); + } +#endif + public void set_anchor (Meta.Window window, Pantheon.Desktop.Anchor anchor) { if (window in panel_windows) { panel_windows[window].anchor = anchor; @@ -255,6 +299,12 @@ public class Gala.ShellClientsManager : Object, GestureTarget { panel_windows[window].request_visible_in_multitasking_view (); } + public void init_greeter (Meta.Window window) { + make_desktop (window); + + wm.override_window_group (window, LOCK_SCREEN_APPLICATION); + } + public void make_centered (Meta.Window window) requires (!is_itself_shell_window (window)) { positioned_windows[window] = new ExtendedBehaviorWindow (window); diff --git a/src/Widgets/LockScreen.vala b/src/Widgets/LockScreen.vala index b7e9325fa..457f50471 100644 --- a/src/Widgets/LockScreen.vala +++ b/src/Widgets/LockScreen.vala @@ -6,10 +6,11 @@ */ public class Gala.LockScreen : Clutter.Actor { - private const WindowGroup[] ALLOWED_WINDOW_GROUPS = { LOCK_SCREEN, LOCK_SCREEN_SHELL, OVERLAY }; + private const WindowGroup[] ALLOWED_WINDOW_GROUPS = { LOCK_SCREEN_APPLICATION, LOCK_SCREEN, LOCK_SCREEN_SHELL, OVERLAY }; public WindowManager wm { get; construct; } + public Clutter.Actor lock_application_group { get; private set; } public Clutter.Actor window_group { get; private set; } public Clutter.Actor shell_group { get; private set; } @@ -24,10 +25,12 @@ public class Gala.LockScreen : Clutter.Actor { var background = new BackgroundContainer (wm.get_display ()); background.add_effect (new BlurEffect (background, 18)); + lock_application_group = new Clutter.Actor (); window_group = new Clutter.Actor (); shell_group = new Clutter.Actor (); add_child (background); + add_child (lock_application_group); add_child (window_group); add_child (shell_group); diff --git a/src/WindowManager.vala b/src/WindowManager.vala index bb00be4b5..5712e30f0 100644 --- a/src/WindowManager.vala +++ b/src/WindowManager.vala @@ -1086,6 +1086,7 @@ namespace Gala { private Clutter.Actor get_window_group_actor (WindowGroup group) { switch (group) { case DESKTOP_SHELL: return shell_group; + case LOCK_SCREEN_APPLICATION: return lock_screen.lock_application_group; case LOCK_SCREEN: return lock_screen.window_group; case LOCK_SCREEN_SHELL: return lock_screen.shell_group; case MODAL: return modal_group.window_group; From 0ff79eb7155dcf7bfb3322c3cb95e4386156ab70 Mon Sep 17 00:00:00 2001 From: lenemter Date: Wed, 8 Jul 2026 03:02:27 +0300 Subject: [PATCH 2/2] Reveal Greeter and Wingpanel in sync when starting --- data/io.elementary.desktop.wm.shell | 6 ++- protocol/pantheon-desktop-shell-v1.xml | 8 --- src/PantheonShell.vala | 1 - src/ShellClients/GreeterWindow.vala | 30 +++++++++++ src/ShellClients/PanelWindow.vala | 2 +- src/ShellClients/ShellClientsManager.vala | 54 ++++++++++++------- .../WindowWithStartAnimation.vala | 10 ++++ src/meson.build | 2 + 8 files changed, 82 insertions(+), 31 deletions(-) create mode 100644 src/ShellClients/GreeterWindow.vala create mode 100644 src/ShellClients/WindowWithStartAnimation.vala diff --git a/data/io.elementary.desktop.wm.shell b/data/io.elementary.desktop.wm.shell index 9f98f1c5c..d2a975b10 100644 --- a/data/io.elementary.desktop.wm.shell +++ b/data/io.elementary.desktop.wm.shell @@ -1,17 +1,18 @@ [io.elementary.wingpanel] launch-on-x=true args=io.elementary.wingpanel -wait-for-n-panels=1 +wait-for-n-windows=1 session-type=desktop [io.elementary.dock] launch-on-x=true args=io.elementary.dock -wait-for-n-panels=1 +wait-for-n-windows=1 session-type=desktop [Greeter wingpanel] args=io.elementary.wingpanel;-g +wait-for-n-windows=1 session-type=greeter [Greeter Session Manager] @@ -20,6 +21,7 @@ session-type=greeter [Greeter] args=io.elementary.greeter +wait-for-n-windows=1 session-type=greeter [Greeter Settings Daemon] diff --git a/protocol/pantheon-desktop-shell-v1.xml b/protocol/pantheon-desktop-shell-v1.xml index 8ed3ae144..115208efc 100644 --- a/protocol/pantheon-desktop-shell-v1.xml +++ b/protocol/pantheon-desktop-shell-v1.xml @@ -28,14 +28,6 @@ - - - Create a greeter surface from an existing surface. - - - - - Create a desktop-specific surface from an existing surface. diff --git a/src/PantheonShell.vala b/src/PantheonShell.vala index af82298ab..94ec58e91 100644 --- a/src/PantheonShell.vala +++ b/src/PantheonShell.vala @@ -31,7 +31,6 @@ namespace Gala { wayland_pantheon_shell_interface = { get_panel, get_widget, - get_greeter, get_extended_behavior, get_greeter, }; diff --git a/src/ShellClients/GreeterWindow.vala b/src/ShellClients/GreeterWindow.vala new file mode 100644 index 000000000..1e4269d2e --- /dev/null +++ b/src/ShellClients/GreeterWindow.vala @@ -0,0 +1,30 @@ +/* + * Copyright 2026 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Leo "lenemter" + */ + +public class Gala.GreeterWindow : PositionedWindow, WindowWithStartAnimation { + + public GreeterWindow (Meta.Window window) { + Object (window: window); + } + + public void animate_start () { + InternalUtils.wait_for_window_actor (window, (window_actor) => { + window_actor.opacity = 0; + window_actor.save_easing_state (); + window_actor.set_easing_mode (Clutter.AnimationMode.EASE_IN); + window_actor.set_easing_duration (Utils.get_animation_duration (200u)); + window_actor.opacity = 255; + window_actor.restore_easing_state (); + }); + } + + protected override void get_window_position (Mtk.Rectangle window_rect, out int x, out int y) { + // We assume Greeter window is maximized + x = window_rect.x; + y = window_rect.y; + } +} diff --git a/src/ShellClients/PanelWindow.vala b/src/ShellClients/PanelWindow.vala index a80076704..9a3d75b33 100644 --- a/src/ShellClients/PanelWindow.vala +++ b/src/ShellClients/PanelWindow.vala @@ -5,7 +5,7 @@ * Authored by: Leonhard Kargl */ -public class Gala.PanelWindow : ShellWindow, RootTarget { +public class Gala.PanelWindow : ShellWindow, RootTarget, WindowWithStartAnimation { private static HashTable window_struts = new HashTable (null, null); public Clutter.Actor? actor { get { return (Clutter.Actor) window.get_compositor_private (); } } diff --git a/src/ShellClients/ShellClientsManager.vala b/src/ShellClients/ShellClientsManager.vala index 19429ac22..2c2d924d0 100644 --- a/src/ShellClients/ShellClientsManager.vala +++ b/src/ShellClients/ShellClientsManager.vala @@ -26,9 +26,11 @@ public class Gala.ShellClientsManager : Object, GestureTarget { private NotificationsClient notifications_client; private ManagedClient[] protocol_clients = {}; - private int starting_panels = 0; + private int starting_windows = 0; + private GLib.HashTable windows_waiting_for_start = new GLib.HashTable (null, null); private GLib.HashTable panel_windows = new GLib.HashTable (null, null); + private GLib.HashTable greeter_windows = new GLib.HashTable (null, null); private GLib.HashTable positioned_windows = new GLib.HashTable (null, null); private GLib.HashTable monitor_label_windows = new GLib.HashTable (null, null); private IBusCandidateWindow? ibus_candidate_window = null; @@ -113,7 +115,7 @@ public class Gala.ShellClientsManager : Object, GestureTarget { } try { - starting_panels += key_file.get_integer (group, "wait-for-n-panels"); + starting_windows += key_file.get_integer (group, "wait-for-n-windows"); } catch (Error e) { warning ("Failed to check how many panels should be awaited, assuming 0: %s", e.message); } @@ -128,16 +130,22 @@ public class Gala.ShellClientsManager : Object, GestureTarget { } private void on_failsafe_timeout () { - if (starting_panels > 0) { - warning ("%d panels failed to start in time, showing the others", starting_panels); + if (starting_windows > 0) { + warning ("%d staring windows failed to start in time, showing the others", starting_windows); - starting_panels = 0; - foreach (var window in panel_windows.get_values ()) { - window.animate_start (); - } + animate_all_starting_windows (); } } + private void animate_all_starting_windows () { + starting_windows = 0; + foreach (var window in windows_waiting_for_start.get_values ()) { + window?.animate_start (); + } + + windows_waiting_for_start.remove_all (); + } + public void make_dock (Meta.Window window) { #if HAS_MUTTER49 window.set_type (Meta.WindowType.DOCK); @@ -236,6 +244,7 @@ public class Gala.ShellClientsManager : Object, GestureTarget { // TODO: Return if requested by window that's not a trusted client? panel_windows[window] = new PanelWindow (wm, window, anchor); + windows_waiting_for_start[window] = panel_windows[window]; if (SessionSettings.is_greeter ()) { wm.override_window_group (window, LOCK_SCREEN_SHELL); @@ -243,25 +252,25 @@ public class Gala.ShellClientsManager : Object, GestureTarget { wm.override_window_group (window, DESKTOP_SHELL); } - InternalUtils.wait_for_window_actor_visible (window, on_panel_ready); + InternalUtils.wait_for_window_actor_visible (window, on_starting_window_ready); // connect_after so we make sure the PanelWindow can destroy its barriers and struts window.unmanaging.connect_after ((_window) => panel_windows.remove (_window)); } - private void on_panel_ready (Meta.WindowActor actor) { - if (starting_panels == 0) { - panel_windows[actor.meta_window].animate_start (); + private void on_starting_window_ready (Meta.WindowActor actor) { + if (starting_windows == 0) { + unowned var window = actor.meta_window; + windows_waiting_for_start[window]?.animate_start (); + windows_waiting_for_start.remove (window); return; } - starting_panels--; - assert (starting_panels >= 0); + starting_windows--; + assert (starting_windows >= 0); - if (starting_panels == 0) { - foreach (var window in panel_windows.get_values ()) { - window.animate_start (); - } + if (starting_windows == 0) { + animate_all_starting_windows (); } } @@ -292,7 +301,7 @@ public class Gala.ShellClientsManager : Object, GestureTarget { public void request_visible_in_multitasking_view (Meta.Window window) { if (!(window in panel_windows)) { - warning ("Set anchor for window before visible in mutltiasking view."); + warning ("Set anchor for window before visible in multitasking view."); return; } @@ -302,7 +311,14 @@ public class Gala.ShellClientsManager : Object, GestureTarget { public void init_greeter (Meta.Window window) { make_desktop (window); + greeter_windows[window] = new GreeterWindow (window); + windows_waiting_for_start[window] = greeter_windows[window]; + wm.override_window_group (window, LOCK_SCREEN_APPLICATION); + + InternalUtils.wait_for_window_actor_visible (window, on_starting_window_ready); + + window.unmanaging.connect_after ((_window) => greeter_windows.remove (_window)); } public void make_centered (Meta.Window window) requires (!is_itself_shell_window (window)) { diff --git a/src/ShellClients/WindowWithStartAnimation.vala b/src/ShellClients/WindowWithStartAnimation.vala new file mode 100644 index 000000000..22a921d19 --- /dev/null +++ b/src/ShellClients/WindowWithStartAnimation.vala @@ -0,0 +1,10 @@ +/* + * Copyright 2026 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Leo "lenemter" + */ + +public interface Gala.WindowWithStartAnimation { + public abstract void animate_start (); +} diff --git a/src/meson.build b/src/meson.build index 1395c66a8..b3f028c2a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -46,6 +46,7 @@ gala_bin_sources = files( 'HotCorners/HotCorner.vala', 'HotCorners/HotCornerManager.vala', 'ShellClients/ExtendedBehaviorWindow.vala', + 'ShellClients/GreeterWindow.vala', 'ShellClients/HideTracker.vala', 'ShellClients/IBusCandidateWindow.vala', 'ShellClients/ManagedClient.vala', @@ -55,6 +56,7 @@ gala_bin_sources = files( 'ShellClients/PositionedWindow.vala', 'ShellClients/ShellClientsManager.vala', 'ShellClients/ShellWindow.vala', + 'ShellClients/WindowWithStartAnimation.vala', 'Widgets/DwellClickTimer.vala', 'Widgets/LockScreen.vala', 'Widgets/MultitaskingView/MonitorClone.vala',